Effective C++笔记(1)
本文最后更新于 654 天前,其中的信息可能已经有所发展或是发生改变。

让自己习惯C++

条款01: 视C++为一个语言联邦

C++主要的4个次语言

  1. C Blocks; statements; preprocessor; built-in data types; arrays; pointers; …
  2. Object-Oriented C++ Classes; encapsulation(封装); inheritance; polymorphism virtual; …
  3. Template C++ Generic programming
  4. STL Containers; iterators; algorithms; function objects; …

每个层次应该有自己的最佳实践。例如对于C层次,传入函数最佳的实践应该是传入值(pass-by-value),而不是指针(pass-by-reference),而对于C with classes层次,则以传递引用为最佳的实践。

条款02: 尽量以const, enum, inline 替代 #define

宏常量用全局的const或者enum来替换

当你在一个类内声明某变量,但你的编译器不允许在声明时赋值初始化,同时接下来的某个语句却需要用到这个变量的具体数值,例如:

int noPlayer;
int scores[noPlayer];

此时编译器便会报错,需要在编译期间noPlayer有具体数值,这个时候就需要使用如下技巧:

enum {noPlayer = 5};
int scores[noPlayer];

这样编译器就能顺利通过,因为enum可以被当成int类型来使用 但注意enum类型在内存中没有实体,无法取得enum类型的地址,因此这个方法更相当于取一个本地的#define数值

宏函数用inline修饰的函数来替换

template<typename T>
inline void callWithMax(const T &a, const T &b)
{
   a > b ? a : b;
}
​
// 
// #define CALL_WITH_MAX(a, b) ((a) > (b) ? (a) : (b))
​
int main()
{
   int a = 5, b = 0;
   // a被累加几次取决于被拿来跟谁比较
   // CALL_WITH_MAX(++a, b);      // a被累加2次
   // CALL_WITH_MAX(++a, b + 10); // a被累加1次
   callWithMax(++a, b);
   callWithMax(++a, b + 10); 
   return 0;
}

条款03: 尽可能使用const

char greeting[] = "Hello";
char* p = greeting;             // non-const pointer, non-const data;
const char* p = greeting;       // non-const pointer, const data;
char* const p = greeting;       // const pointer, non-const data;
const char* const p = greeting; // const pointer, const data;
void f1(const Widget* pw);
void f2(Widget const* pw); // f1 == f2
const vector<int>::iterator iter;  // iter == T* const
vector<int>::const_iterator citer; // citer == const T* (定义某迭代器指向一个常数)

令函数返回常量值往往可以降低客户错误而造成的意外,而又不至于放弃安全性和高效性。

class Rational{....};
Rational operator*(const Rational& lhs, const Rational& rhs){...}
​
Rational a,b,c;
if(a * b = c){......} //error
​
// 开头的 const 可以预防无意义的赋值动作(也就是防止将“==”写成“=”造成的错误)
const Rational operator*(const Rational& lhs, const Rational& rhs){...} 

给成员函数使用const关键字是非常重要的,它可以让接口更加直观,直接告诉用户这个函数是不是只读(Read only),会不会改变某变量。更重要的是,用const修饰的对象只能调用const修饰的成员函数,因为不被const修饰的成员函数可能会修改其他成员数据,打破const关键字的限制。因此,需要同时声明有const和没有const的成员函数,例如:

const char& operator[](size_t pos) const;
// 函数前const:普通函数或成员函数(非静态成员函数)前均可加const修饰,表示函数的返回值为const,不可修改。
// 函数后加const:只有类的非静态成员函数后可以加const修饰,表示该类的this指针为const类型,不能改变类的成员变量的值,即成员变量为read only
char& operator[](size_t pos);

下面代码中length()函数要做某些错误检测,因此可能会修改成员数据。即使真正的功能核心只是返回字符长度,编译器依然认为你可能会修改某些成员数据而报错。

class Text{
 public:
   std::sizt_t length() const;
 private:
   char* pText;
   std::size_t length;
   bool lengthValid;
....
};
​
std::size_t Text::length() const{
 if(!lengthValid){               //做某些错误检测
   length = std::strlen(pText);  //error! 在const成员函数中
   lengthValid = true;           //不能赋值给textLength        
}        //和lengthIsValid
​
 return length;                  
}

逻辑常量性(Logical constness),即允许某些数据被修改,只要这些改动不会反映在外,例如,以上问题可以用mutable关键字来解决。

mutable std::size_t length;
mutable bool lengthValid;

在定义常量与非常量成员函数时,避免代码重复

代码复制一遍,既不显得美观,又增加了代码维护的难度和编译时间。因此,我们可以使用非常量的函数来调用常量函数。(如果使用相反的方法,用const函数来调用non-const函数,就可能会有未知结果,因为这样相当于non-const函数接触到了const对象的数据,就可能导致常量数据被改变。)

const char& operator[](std::size_t pos) const{....}
char& operator[](std::size_t pos){
 return
   const_cast<char&>(                              //const_cast去掉const关键字,并转换为char&
     static_cast<const Text&>(*this)[position];    //给当前变量加上const关键字,才可以调用const操作符
);
}

为了避免无限递归调用当前非常量的操作符,我们需要将(*this)转换为const Text&类型才能保证安全调用const的操作符,最后去掉const关键字再将其返回,巧妙避免了代码的大段复制。

条款04: 确定对象被使用前已被初始化

⚠️assignment和initialization的区别

对象的成员变量的初始化动作发生在进入构造函数本体之前。

class A {...};
class B {
private:
  int x;
  int y;
  const std::list<double>& num;
public:
  B(const int& _x, const int& _y, const std::list<double>& num;){
    x = _x; // assignments (NOT initializations)
    y = _y;
    num = _num;
  }
}

所以最好使用member initialization list替换赋值动作,结果相同但通常效率更高:

B::B(const int& _x, const int& _y, const std::list<double>& num;)
:x(_x), 
 y(_y),
 num(_num)
{}

构造函数是可以被重载(overload)的,还需要一个没有参数输入的默认构造函数,可以定义:

B::B():x(0), y(0), num() {}
//num()调用了std::list<double>类型的默认构造函数

在定义引用(reference)和常量(const)时,不将其初始化会导致编译器报错

const int a;                //error! 需要初始化!
int& b;                     //error! 需要初始化!
​
const int a = 3;            //编译通过
int c = 3;
int& b = c;                 //编译通过!

C+成员初始化次序:

  1. base classes早于其derived classes被初始化;
  2. class的成员变量总是以其声明次序被初始化;
class myClass{
  private:
    int a;
    int b;
    int c;
  public:
    myClass(int _a, int _b, int _c);
};

//注意,即使初始化列表是c->b->a的顺序,真正的初始化顺序还是按照a->b->c
myClass::myClass(int _a, int _b, int _c): c(_c), a(_a), b(_b) {}

static: 寿命从被构造出来直到程序结束为止;
local static: 函数内生命的static对象;
non-local static: 其余static对象。

程序结束时static对象会被自动销毁,也就是他们的析构函数会在main()结束时被自动调用。

编译单元(translation unit): 可以让编译器生成代码的基本单元,一般一个源代码文件就是一个编译单元。

非本地静态对象(non-local static object): 静态对象可以是在全局范围定义的变量,在名空间范围定义的变量,函数范围内定义为static的变量,类的范围内定义为static的变量,而除了函数中的静态对象是本地的,其他都是非本地的。

C++对定义于不同编译单元内的non-local static对象的初始化次序无明确定义。

此外注意,静态对象存在于程序的开始到结束,所以它不是基于堆(heap)或者栈(stack)的。初始化的静态对象存在于.data中,未初始化的则存在于.bss中。

现在有一种特殊情况,尤其是在大型项目中比较普遍:在两个编译单元中,分别包含至少一个非本地静态对象,当这些对象发生互动时,它们的初始化顺序是不确定的,所以直接使用这些变量,就会给程序的运行带来风险。那如何初始化非本地静态对象

解决方法: 将非本地的静态变量变为本地静态变量

使用一个函数,只用来定义一个本地静态变量并返回它的引用。因为C++规定在本地范围(函数范围)内定义某静态对象时,当此函数被调用,该静态变量一定会被初始化。

class Server{...};
​
Server& server(){                //将直接的声明改为一个函数
   static Server server;
   return server;
}
class Client{...};
​
Client::client(){               //客户端构造函数通过函数访问服务器数据
   number = server().number;
}
​
Client& client(){               //同样将客户端的声明改为一个函数
   static Client client;       //定义并初始化local static对象
   return client;//返回一个reference指向上述对象
}

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 协议 。转载请注明出处!
您可以通过 RSS 订阅本站文章更新。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇