第一课 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。
注意:
- 所有的编译器自动生成的函数都是public且inline
- 当它们需要被生成时(程序的其他地方调用了它们),编译器才会生成它们。
第六课 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。
- 解决方案:
- 将try catch加入destructor。
- 将容易触发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里调用虚拟函数。