Effective C++ 阅读笔记·Accustoming Yourself to C++

01 View C++ as a federation of languages

    C++四大组成部分:

  • C;
  • Object-Oriented C++;
  • Template C++;
  • STL;

02 Perfer consts, enum, and inlines to #defines

  • class内常量已在声明时设置初值,则在定义时不可以再设初值;
  • #defines不重视作用域,不能够用来定义class专属常量,也不能提供封装性;
  • 枚举类型的数值可充当ints被使用(the enum hack);
class GamePlayer{
private:
    enum{NumTurns=5};
    int scores[NumTurns];
    ...
};
  • 定义宏时,必须为宏中的所有实参加上小括号,避免调用出现问题,但仍有风险;
#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))

风险示例:
int a=5, b=0;
CALL_WITH_MAX(++a, b);     //a被累加两次
CALL_WITH_MAX(++a, b+10);  //a被累加一次
  • 对于单纯常量,最好用const对象或enums替换#defines
  • 对于形似函数的宏(macros),最好改用inline函数替换#defines
template<typename T>
inline void callWithMax(const T& a, const T& b){
    f(a > b ? a : b)
}

03 Use const whenever possible

  • STL迭代器是以指针为根据操作的,迭代器的作用相当于一个T*指针;声明迭代器为const相当于声明指针为const,即声明一个T* const指针,是一个指针常量;const_iterator 相当于const T* 指针,是一个常量指针,指向的内容不可改变;
std::vector<int> vec;
...
const std::vector<int>::iterator iter = vec.begin(); //iter相当于一个T* const
*iter = 10;   //可以,所指向的内容可以改变
++iter;       //错误,指针常量不可改变
std::vector<int>::const_iterator cIter = vec.begin(); //cIter相当于一个const T*
*cIter = 10;  //错误,常量指针,所指向的内容是常量
++cIter;      //可以,指针变量可以改变
  • const 修饰成员变量
    (1) 如果const位于*左侧,表示指针所指数据是常量,不能修改该数据;指针本身是变量,可以指向其他的内存单元;
    (2) 如果const位于*右侧,表示指针本身是常量,不能指向其他内存地址;指针所指的数据可以修改;
    (3) 两个const,*左右各一个,表示指针和指针所指数据都不能修改;
  • const修饰函数参数
    (1) 如果输入的参数采用“指针传递”,那么加上const可以防止意外的改动该指针,起到保护作用,例如:void func(char *dest_str, const char *src_str)
    (2) 如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就不需要保护,所以不需要加const,例如:void func(int x)不需要写成void func(const int x)
    (3) 但是对于非内部的数据类型的参数而言,例如void func( A a)这样声明的函数注定效率低,因为函数体内将自动产生临时对象,用于复制该参数a,而临时变量的构造,拷贝构造,析构都将耗费时间。所以为了提高效率可以写成这样void func(A &a)用“引用传递”就不用产生临时对象,如果不想改变参数a就写成void func(const A &a)

  • const修饰成员函数
    (1) const成员函数内不能修改任何的成员变量(mutable修饰的变量除外);
    (2) const成员函数不能调用非const成员函数,因为非const成员函数可以会修改成员变量;(注意const成员函数的声明是将const放到函数的尾部,例如: int func(void)const{}

  • const修饰函数返回值
    (1) 使得函数调用表达式不能作为左值。用const来修饰返回的指针或引用,保护指针指向的内容或引用的内容不被修改,常用于运算符重载;
    (2) 以“指针传递”方式的函数返回值加上const修饰,那么该函数的返回值的内容不能被修改,例如:const char *get_string(void)
    (3) 如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部的临时存储单元中,所以加上const修饰没有意义,例如:int get_number(void)不需要写成const int get_number(void)
    (4) 如果返回值不是内部数据类型,例如:A get_string(void),这样会产生一个临时的对象用来存放返回的数据,会调用拷贝构造函数,这样效率会低,可以采用“引用传递”A &get_string(void),如果加上const那么返回值的内容就不会被改变const A &get_string(void)

04 Make sure that objects are initialized before they’re used

  • 永远在使用对象之前先将它初始化;
  • 确保每一个构造函数都将对象的每一个成员初始化;
  • C++中,对象的成员变量的初始化动作发生在进入构造函数本体之前;
  • 赋值(assignment)与初始化(initialization)的区别:
//赋值
class AB{
public: 
    AB(const std::string& name, const std::string& address);
private: 
    std::string theName;
    std::string theAddress;
};
AB::AB(const std::string& name, const std::string& address){
    theName = name;
    theAddress = address;
}

//初始化(成员初值列member initialization list)
AB::AB(const std::string& name, const std::string& address):theName(name), theAddress(address){}
  • class的成员变量总是以其声明次序被初始化;
  • 为内置对象(比如int,double,float等)进行手工初始化,因为C++不保证初始化它们;
  • STL容器拥有默认构造函数,所以会自动初始化成默认值,可以直接使用;
  • 构造函数最好使用成员初值列,而不是在构造函数本体内使用赋值操作。并且,初值列列出的成员变量,其次序应该和它们在class中的声明次序相同;
  • 为避免“跨编译单元之初始化次序”问题,需用local static对象替换non-local static对象;
    相关博客:【1】【2】
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值