您的位置:首页 > 编程语言 > C语言/C++

读《高质量C++编程》总结

2014-04-26 17:30 253 查看


今天翻了一遍《高质量C++编程》,为了以后再次回顾,做了如下记录与一些自己的总结。

1.头文件包含问题

为了防止头文件被重复引用,应当用 ifndef/define/endif 结构产生预处理块。

也可以使用另一种方式:#pragma once 相信很多人知道,作用和上面是一样的。

2.循环语句效率

在多重循环中,如果可以,应当将最长的循环放在最内层,最短的循环放在最外层,以减少 CPU 跨切循环层的次数

如:

for(i = 0; i<500; i++)

{

for(j=0; j<10; j++)

{

//xxx

}

}//不良写法

应改为:

for(i = 0; i<10; i++)

{

for(j=0; j<500; j++)

{

//xxx

}

}

另外,如果循环体内存在逻辑判断,并且循环次数很大,可以将逻辑判断移到循环体的外面。



3.switch语句

Java中

1. byte、char、short、int四种基本类型以及它们的包装类(需要Java5.0/1.5以上版本支持)都可以用于switch语句。

2. long、float、double、boolean四种基本类型以及它们的包装类(在Java所有版本中)都不能用于switch语句。

3. enum类型,即枚举类型可以用于switch语句,但是要在Java5.0(1.5)版本以上才支持。

4. 所有类型的对象(包括String类,但在Java5.0/1.5以上版本中,该项要排除byte、char、short、int四种基本类型对应的包装类)都不能用于switch语句。



C++中

1. char、short、int、long、bool四种基本类型都可以用于switch语句。

2. float、double都不能用于switch语句。

3. enum类型,即枚举类型可以用于switch语句。
4. 所有类型的对象都不能用于switch语句。(比如string,可以自己试试)

4.常量
1.不能在类声明中初始化 const 数据成员
class A

{…

const int SIZE = 100; // 错误,企图在类声明中初始化 const 数据成员

int array[SIZE]; // 错误,未知的 SIZE

};

2.const 数据成员的初始化只能在类构造函数的初始化表中进行
class A

{…

A(int size); // 构造函数

const int SIZE ;

};

A::A(int size) : SIZE(size) // 构造函数的初始化表

{



}

3.C++中的常量用const声明,C语言中用的是#define。
const最大的用处是用在函数的参数修饰,比如函数的一个参数是指针,如果这个函数在以后的工作中不会改变指针指向的变量的话,要在指针前加const,避免以后意外的改变。

5.static成员变量与函数
static类型的类成员变量必须在类外初始化;
static成员函数不依赖于类,相当于类里的全局函数(可以由该类对象调用,也可以 类名::函数名()的形式调用)
static成员函数相当于把访问范围限制在所在的类中! 注意:不能访问类中非static成员变量以及成员函数。
static成员函数在实际中可以用作线程函数

6.指针与引用
如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率。而有些场合只能用“值传递”而不能用“引用传递” ,否则会出错。

例如:

class String
{…
    //  赋值函数
    String & operate=(const String &other);  
    //  相加函数,如果没有 friend 修饰则只许有一个右侧参数
    friend  String operate+( const String &s1, const String &s2); 
private:
    char *m_data; 
}:


String 的赋值函数 operate = 的实现如下:
String& String::operate=(const String &other)
{
    if (this == &other)
           return *this;
    delete m_data; 
    m_data = new char[strlen(other.data)+1];
    strcpy(m_data, other.data);
    return *this;   //  返回的是 *this 的引用,无需拷贝过程
}


对于赋值函数,应当用“引用传递”的方式返回 String 对象。如果用“值传递”的

方式,虽然功能仍然正确,但由于 return 语句要把*this 拷贝到保存返回值的外部存

储单元之中,增加了不必要的开销,降低了赋值函数的效率。

String 的相加函数 operate + 的实现如下:

String operate+(const String &s1, const String &s2)  
{
    String  temp;
    delete temp.data;  // temp.data 是仅含‘\ 0’ 的字符串
    temp.data = new char[strlen(s1.data) + strlen(s2.data) +1];
    strcpy(temp.data, s1.data);
    strcat(temp.data, s2.data);
    return temp;
}


对于相加函数,应当用“值传递”的方式返回 String 对象。如果改用“引用传递”,

那么函数返回值是一个指向局部对象 temp 的“引用”。 由于 temp 在函数结束时被自动销毁,将导致返回的“引用”无效

7.extern c的问题

如果 C++ 程序要调用已经被编译后的 C 函数,该怎么办?

假设某个 C 函数的声明如下:

void foo(int x, int y);

该函数被 C 编译器编译后在库中的名字为_foo,而 C++编译器则会产生像_foo_ int_ int之类的名字用来支持函数重载和类型安全连接。由于编译后的名字不同,C++ 程序不能直接调用 C 函数。C++ 提供了一个 C 连接交换指定符号 extern “C”来解决这个问题。

例如:

extern “C”

{

void foo(int x, int y);

… // 其它函数

}

或者写成

extern “C”

{

#include “myheader.h”

… // 其它 C 头文件

}

这就告诉 C++ 编译译器,函数 foo 是个 C 连接,应该到库中找名字_foo 而不是找

_foo_int _ int 。C++ 编译器开发商已经对 C 标准库的头文件作了 extern “C”处理,所以我们可以用#include 直接引用这些头文件。

8.参数缺省值

参数缺省值只能出现在函数的声明中,而不能出现在定义体中。

例如:

void Foo(int x=0, int y=0); // 正确,缺省值出现在函数的声明中

void Foo(int x=0, int y=0) // 错误,缺省值出现在函数的定义体中

{



}

9.运算符重载

如果运算符被重载为全局函数,那么只有一个参数的运算符叫做一元运算符,有两个参数的运算符叫做二元运算符。

如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个右侧参数,因为对象自己成了左侧参数



10.重载、覆盖与隐藏

成员函数被重载的特征:

(1 )相同的范围(在同一个类中);

(2 )函数名字相同;

(3 )参数不同;

(4 )virtual 关键字可有可无。

覆盖是指派生类函数覆盖基类函数,特征是:

(1 )不同的范围(分别位于派生类与基类);

(2 )函数名字相同;

(3 )参数相同;

(4 )基类函数必须有 virtual 关键字。

隐藏:

(1 )如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual

关键字,基类的函数将被隐藏
(注意别与重载混淆)。

(2 ) 如果派生类的函数与基类的函数同名, 并且参数也相同, 但是基类函数没有 virtual

关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

(未完待续...)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: