Effective C++
作者:互联网
目录
规则2:尽量使用const、enum、inline替换#define
2、定义一个类的专属常量:在类内使用const和static
需要在实现文件中再定义一次已经在类内声明了的static变量
3、令函数返回一个const对象,往往可以避免客户因为错误使用而造成的意外,如下面的代码:
4、const类内成员函数:在函数的参数列表后加上一个const---mutable引入
使用const_cast为对象去掉const属性和使用static_cast为对象加上const属性的方法
规则2:尽量使用const、enum、inline替换#define
1、使用const替换#define
1 #define ASPECT_RATIO 1.653 //大写常用语宏,但是每次出现ASPECT_RATIO的地方,预处理器都会盲目的将ASPECT_RATIO替换为1.653,导致目标代码出现多个1.653 2 const double AspectRatio = 1.653; //此时在目标代码中只出现一个存储1.653的地方
2、定义一个类的专属常量:在类内使用const和static
1 class GamePlayer{ 2 private: 3 static const int NumTurns = 5; //static确保了所有类对象共享NumTurns(NumTurns只有一份)、const确保了NumTurns不可以被修改 4 int scores[NumTurns]; 5 }; //放在头文件中
需要在实现文件中再次定义在类中已经声明了的static变量的情况:
上面是NumTurns的声明式而非定义式。通常C++要求你给任何东西提供一个定义式,但如果它是一个类内static、const变量,只要不取它的地址,则可以声明并使用它而无需提供定义式。但是你需要取它的地址或编译器不明确的简直要看到一个定义式,那么需要提供下面的定义式:
1 class GamePlayer{ 2 private: 3 static const int NumTurns; 4 int scores[NumTurns]; 5 }; //放在头文件中 6 const int GamePlayer::NumTurns = 5; //NumTurns的定义式,放在实现文件而非头文件中,在定义式中获得初值
上述代码存在的问题:万一你的编译器错误的不允许类内static、const变量完成初值设定,而此时类内的scores需要使用该变量以确定数组内元素的个数,此时可以使用枚举:
1 class GamePlayer{ 2 private: 3 enum {NumTurns = 5}; //注意此时NumTurns是一个枚举,此时对NumTurns取地址是不合法的 4 int scores[NumTurns]; 5 }; //放在头文件中
3、使用inline定义内联函数,替换#define
引入:
1 #define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b)) //f是只带一个参数的函数 2 int a = 5, b = 0; 3 CALL_WITH_MAX(++a,b); //在判断(++a) > (b)的时候a被累加一次,发现成立,然后再执行(++a),并将++a的结果做为f的参数。即a被累加了两次 4 CALL_WITH_MAX(++a,b+10); //在判断(++a) > (b)的时候a被累加一次,发现不成立,然后将b做为f的参数。即a被累加了一次
在调用函数f之前,a的递增此时居然取决于它被拿来和谁比较,可以使用内联函数和模板来代替:
1 template<typename T> 2 inline void callWithMax(const T & a, const T & b){ 3 f(a > b ? a : b); 4 }
规则3:尽量可能使用const
1、const用于指针中
1 char greeting1[] = "Hello"; 2 char greeting2[] = "GoodMorning"; 3 char* p = greeting1; //non-const pointer(p可以改变指向,p=greeting2合法),non-const data(p[0]='G'合法) 4 const char* p = greeting1; //non-const pointer(p可以改变指向,p=greeting2合法),const data(p[0]='G'不合法) 5 char* const p = greeting1; //const pointer(p不可以改变指向,p=greeting2不合法),non-const data(p[0]='G'合法) 6 const char* const p = greeting1; //const pointer(p不可以改变指向,p=greeting2不合法),non-const data(p[0]='G'不合法)
其中const char* p = greeting1; 也可以写成:
char const * p = greeting1;
2、const用于STL中的迭代器中
1 const用于迭代器表示这个迭代器不可以指向不同的东西,但是它指向的东西是可以改变的,例如: 2 std::vector<int> vec; 3 ... 4 const std::vector<int>::iterator iter = vec.begin(); //表示iter只可以指向vec容器内的第一个元素,不可以指向其他的元素,但是iter指向的元素本身是可以被改变的 5 *iter = 10; //合法 //等价于T* const (char* const) 6 ++iter; //不合法
如果你希望迭代器所指向的东西不可以被改变,那么可以使用const_iterator:
1 std::vector<int> vec; 2 ... 3 const std::vector<int>::const_iterator iter = vec.begin(); //等价于const T* (const char*) 4 *iter = 10; //不合法 5 ++iter; //合法
3、令函数返回一个const对象,往往可以避免客户因为错误使用而造成的意外,如下面的代码:
1 class Rational{...}; 2 const Rational operator*(const Rational & lhs, const Rational & rhs);
返回一个const Rational类对象的原因是避免客户出现下面的错误:
1 int main(){ 2 Rational a,b,c; 3 ... 4 (a*b)=c; 5 (a*b)将调用operator*()函数,然后返回一个const类型的Rational类对象,然后将c赋给该const Rational类对象是不合法的! 6 }
需要补充个的知识点:
1 const int num = 5; 2 num = 6; //不合法!!
4、const类内成员函数:在函数的参数列表后加上一个const
4.1两个成员函数如果只是常量性不同,则可以被重载,举例如下:
1 class TextBlock{ 2 public: 3 .... 4 const char & operator[](std::size_t position) const{ //类内const成员函数 5 return text[position]; 6 } 7 const char & operator[](std::size_t position){ //类内non-const成员函数 8 return text[position]; 9 } 10 private: 11 std::string text; 12 }; 13 14 int main(){ 15 TextBlock tb("Hello"); 16 std::cout << tb[0]; //调用类内non-const成员函数 17 18 const TextBlock ctb("Hello"); 19 std::cout << ctb[0]; //调用类内const成员函数 20 21 return 0; 22 }
上面的这个例子有点造作,下面的这个例子才是真实环境下的类内const成员函数使用方法:
1 void print(const TextBlock & ctb){ 2 std::cout << ctb[0]; //调用类内const成员函数 3 ... 4 }
对于上面的代码,将私有变量原型的类型string改为char*,在类外使用一个指针指向了类内成员变量,那么就有可能修改该const对象中的变量,如下面的代码:
1 class CTextBlock{ 2 public: 3 .... 4 const char & operator[](std::size_t position) const{ //类内const成员函数 5 return text[position]; 6 } 7 const char & operator[](std::size_t position){ //类内non-const成员函数 8 return text[position]; 9 } 10 private: 11 char* pText; 12 }; 13 int main(){ 14 const CTextBlock cctb("Hello"); 15 char* pc = &cctb[0]; 16 *pc = 'J'; //此时cctb类对象中的*pText=Jello 17 18 return 0; 19 }
上面的程序表明,你创建了一个常量对象并设以初值,并对它只调用const成员函数,但是你最终还是改变了它的值。
写到这里,需要停下来思考一个问题:成员函数如果是const意味着什么?
bitwise constness学派认为:不可以更改对象中任何成员变量(static变量除外)才可以说是const成员函数;
logical constness学派认为:在编译器侦测不出来的情况下,可以修改对象内的某一位,使编译器侦测不来的方法是使用mutable。
不符合bitwise constness观点的代码:
1 class CTextBlock{ 2 3 std::size_t length() const; 4 private: 5 char* pText; 6 std::size_t textLength; //需要在length()类内修改,但是不符合bitwise constness观点 7 bool lengthIValid; //需要在length()类内修改,但是不符合bitwise constness观点 8 }; 9 std::size_t CTextBlock::length() const{ 10 if(!lengthIValid){ 11 textLength = std::strlen(pText); //在const成员函数内修改了类成员,不符合bitwise constness观点 12 lengthIValid = true; //在const成员函数内修改了类成员,不符合bitwise constness观点 13 } 14 return textLength; 15 }
由于不符合bitwise constness观点,以上代码会出现编译错误,使用mutable关键字修改类内需要在const成员函数内修改的类成员变量,修改方法如下:
1 class CTextBlock{ 2 3 std::size_t length() const; 4 private: 5 char* pText; 6 mutable std::size_t textLength; //需要在length()类内修改,故将textLength声明为mutable变量 7 mutable bool lengthIValid; //需要在length()类内修改,故将textLength声明为mutable变量 8 }; 9 std::size_t CTextBlock::length() const{ 10 if(!lengthIValid){ 11 textLength = std::strlen(pText); //在const成员函数内修改了mutable类成员textLength,是合法的 12 lengthIValid = true; //在const成员函数内修改了mutable类成员lengthIValid,是合法的 13 } 14 return textLength; 15 }
如下面的代码,存在代码重复的问题:
1 class TextBlock{ 2 public: 3 .... 4 const char & operator[](std::size_t position) const{ //类内const成员函数 5 ... //边界检验 6 ... //数据访问 7 ... //数据完整性检验 8 return text[position]; 9 } 10 const char & operator[](std::size_t position){ //类内non-const成员函数 11 ... //边界检验 12 ... //数据访问 13 ... //数据完整性检验 14 return text[position]; 15 } 16 private: 17 std::string text; 18 };
于是在const operator[]()中和non-const operator[]()成员函数中,存在着代码重复的问题,解决方法是在non-const operator[]()成员函数中调用const operator[](),那么就需要为该对象加上const属性,然后调用const operator[]()函数,最后再去掉const属性。其中为对象加上const属性方法是使用static_cast,为对象去掉const属性方法是使用const_cast,修改方法如下:
1 class TextBlock{ 2 public: 3 .... 4 const char & operator[](std::size_t position) const{ //类内const成员函数 5 ... //边界检验 6 ... //数据访问 7 ... //数据完整性检验 8 return text[position]; 9 } 10 char & operator[](std::size_t position){ //类内non-const成员函数 11 return 12 const_cast<char &>( //3、将const operator[]()函数返回值的const属性去掉 13 static<const TextBlock &>(*this) //1、由于调用的是non-const属性的operator[](),所以*this的原型是TextBlock &,无const属性,所以static<const TextBlock &>(*this)为*this加上const属性 14 [position] //2、调用const operator[]() 15 ); 16 } 17 private: 18 std::string text; 19 };
为何不可以使用const成员函数调用non-const成员函数?
const成员函数承诺不修改类内变量,但non-const成员函数没有这样的承诺,如果使用const成员函数调用non-const成员函数就有可能发生在const成员函数中修改某一个类内变量是非法的,但是跑到non-const去修改该类内变量就是合法的了。
标签:std,const,函数,Effective,成员,C++,char,类内 来源: https://www.cnblogs.com/YiYA-blog/p/14399047.html