第一课 Const

https://www.youtube.com/watch?v=7arYbAhu0aw&list=PLE28375D4AC946CC3

  • ptr to const(在*左侧) vs. const ptr(在*右侧)
  • 取消常性:const_cast<int&>() vs. 增加常性:static_cast<const int&>()
    • 尽量避免使用
  • 为什么使用Const?
    • 防止inadvert的常量变化
    • Documentation
    • Compiler Optimization
    • ROM使用 (引申话题:RAM vs. ROM)

第二课 Const and Functions

https://www.youtube.com/watch?v=RC7uE_wl1Uc&list=PLE28375D4AC946CC3&index=2

  • Const Reference 作为函数的参数:
    • 该函数内Reference的值不可改变
    • 如果 Const 作为参数用在值传递(pass-by-value)而不是引用传递(pass-by-const), 则没有意义且不能Overload
  • Const 返回值(Return Value)
    • 同理,只有在使用引用作为返回值时才有意义
  • Const Function
    • 不能改变Member Variable; 不能调用没有const的Member Function
    • 可以用来实现Overloading: 当对象未常量时调用该Const函数

第三课 Rvalue vs. Lvalue

https://www.youtube.com/watch?v=UTUdhjzws5g

  • 左值 lvalue(addressable) - 表示了一个占据内存中某个可识别位置的对象。
  • 右值 rvalue - 所有不是左值的表达式都是右值,即不表示内存中某个可识别位置的对象表达式。
  • 例如 int x = 2 中,x 为左值,2为右值。
    • int *p = &(i + 2); => Error
    • i + 2 = 4; => Error
    • 2 = i; => Error
  • 左值引用: int i; int &r = i;
  • 右值引用: int &r = 5; //Error 但可以 const int &r = 5;
  • 左值右值可以互转:
        // lvalue to rvalue example 
        int i = 0;
        int x = i + 2;
        // rvalue to lvalue example
        int v[3];
        *(v+2) = 4;
  • 常见错误理解:
    • 函数和操作符(operator)的结果是右值。反例:函数返回引用,[] operator
    • 所有左值都可赋值修改。反例: const 的左值是不可赋值修改的。其余为可修改左值。
    • 所有右值都不可modify。反例:
        class dog;
        dog().bark(); // bark() may change the state of the dog object.

第四课 Logic Constness and Bitwise Constness

https://www.youtube.com/watch?v=8A5AwX6XExw&list=PLE28375D4AC946CC3&index=3

class BigArray {
    vector<int> v; // huge vector
    mutable int accessCounter;
    int *v2; // 
    public:
       int getItem(int index) const {
           accessCounter++; // mutable
           return v[index];
       }

       void setV2Item(int index, int x)  {
           *(v2+index) = x; //如改为const member function也可编译通过
       }
}

Compiler 只关心Bitwise Constness。需要认真设计来实现logic和bitwise的consistency。
我:get function应该是const的,虽然我改了个member variable。
编译器: 你别整那没用的。我只管你member variable有没有被改。

第五课 Compiler Generated Functions

https://www.youtube.com/watch?v=EL30-a2gblQ&list=PLE28375D4AC946CC3&index=4

  • 天启四骑士(编译器会implicitly自动生成以下函数)
    • Copy Constructor (复制构造函数)
    • Copy Assignment Operator (拷贝赋值运算符)
    • Destructor (析构函数)
    • Default Constructor (默认构造函数)
class dog {
    public:
        dog(const dog &rhs) {...}; // Member by member initialization
        dog &operator=(const dog &rhs) {...}; // Member by member copying
        dog() {...}; // 1. Call the base class's default constructor 2. Call data members' default constructor
        ~dog() {...}; // 1. Call all base class's destructor 2. call data members' destructor
};

当这些函数无法被生成时,则不生成。例:

  • Copy Assignment Operator cannot copy const and reference members
  • base class没有default constructor, 则子函数也无法生成default constructor。

注意

  1. 所有的编译器自动生成的函数都是public且inline
  2. 当它们需要被生成时(程序的其他地方调用了它们),编译器才会生成它们。

第六课 Disallow Functions

https://www.youtube.com/watch?v=ZiNGWHg5Z-o&list=PLE28375D4AC946CC3&index=5

class OpenFile {
    public:
        OpenFile(string filename) {cout << "Open a file " << filename << endl}
        OpenFile(OpenFile &rhs) == delete; // C++ 11 and above
        voiud destroyMe() {delete this;} // calls the private destructor
    private:
        OpenFile(OpenFile &rhs); // C++ 98, make the copy constructor private
        ~OpenFile() {cout << "OpenFile destructed." << endl;} // private destructor 
};
int main() {
    OpenFile f(string("somefile"));
    OpenFile f2(f); // We want to avoid two OpenFile editing one single file => disable Copy constructor
}

*当一个静态对象 out of scope的时候,默认的public destructor会被调用。此时private的destructor用法不合适。当对象为动态(new)时,我们可以通过这种方式强制将对象生成在栈上,来接余额宝贵的堆空间(embedded system)很常用。

第七课 Virtual Destructor and Smart Destructor

https://www.youtube.com/watch?v=ZiNGWHg5Z-o&list=PLE28375D4AC946CC3&index=6

  • 方法一:Virtual Destructor
class Dog {
    public:
    virtual ~Dog() { cout << "Dog destroyed" << endl;} //多态时, 只有设置了virtual destructor, 子类的destuctor才会被调用。
};
class YellowDog {
    public:
    ~YellowDog {cout << "Yellow dog destroyed" << endl;}
};
class DogFactory {
    public: 
    static Dog *createYellowDog() { return new YellowDog(); }
};
int main() {
    Dog *pd = DogFactory::createYellowDog();
    delete pd;
    return 0;
}
  • 方法二:Smart Destructor
class Dog {
    public:
    ~Dog() { cout << "Dog destroyed" << endl;} 
};
class YellowDog {
    public:
    ~YellowDog {cout << "Yellow dog destroyed" << endl;}
};
class DogFactory {
    public: 
    static shared_ptr<Dog> createYellowDog() { return shared_ptr<YellowDog>(new YellowDog()); }
};
int main() {
    shared_ptr<Dog> pd = DogFactory::createYellowDog();
    return 0;
}
*注意,所有的STL Object都没有virtual析构函数,继承时要小心。

第八课 Exceptions in Destructors

https://www.youtube.com/watch?v=LQMYwvM8RF8&list=PLE28375D4AC946CC3&index=7

class dog {
    public: 
    string m_name;
    dog(string name) { m_name = name; cout << name << "is spawn." << endl; }
    ~dog() { cout << m_name << " is defeated.\n" << endl; }
    void bark() { ... }
};
int main() {
    try {
        dog dog1("Texas");
        dog dog2("Lappland");
        throw 20;
        dog1.bark();
        dog2.bark();
    } catch (int e) {
        catch << e << " is caught" << endl;
    }
}

OUTPUT:
Texas is spawn.
Lappland is spawn.
Lappland is defeated.
Texas is defeated.
20 is caught.

class dog {
    public: 
    string m_name;
    dog(string name) { m_name = name; cout << name << "is spawn." << endl; }
    ~dog() { cout << m_name << " is defeated.\n" << endl;  throw 20; }
    void bark() { ... }
};
int main() {
    try {
        dog dog1("Texas");
        dog dog2("Lappland");
        dog1.bark();
        dog2.bark();
    } catch (int e) {
        catch << e << " is caught" << endl;
    }
}
  • 程序会崩溃。因为在进入到catch的scope之前,两个dog都会调用析构函数,届时会有两个pending的exeception。
  • 解决方案:
    1. 将try catch加入destructor。
    2. 将容易触发exception的代码放入另一个函数。

第九课 Virtual Function in Constructor or Destructor

https://www.youtube.com/watch?v=LQMYwvM8RF8&list=PLE28375D4AC946CC3&index=8

class Dog {
    public:
    Dog() {cout << "Dog is born." << endl; bark(); }
    virtual void bard() { cout << "I am just a dog." << endl; }
    void seeCat() { bark(); }
    ~Dog() { bark(); }
};

class YellowDog: public Dog {
    public:
    Yellowdog { cout << "Yellow dog is born" << endl;}
    virtual void bark() { cout << "I am a yellow dog" << endl; } // 若将 virtual 关键字省掉,依旧是虚拟函数。写在这里是为了explicitly声明。
};

int main() {
    Yellodog d;
    d.seeCat(); return 0;
}

Output:
Dog is born.
I am just a dog.
Yellow dog is born.
I am a yellow dog.
I am just a dog.

尽量避免在Constructor和Destructor里调用虚拟函数。

第十课 Resource Acquisition is Initialization