尽可能使用const可能会让你多写很多个const,而且会在你试图改变被const修饰的“变量”时报告一大堆bugs。然而,如果没有了它们,就成了掩耳盗铃了吧?后果是不是不堪设想呢?很简单,你只需要看看const修饰的是谁,就知道谁不能被改变了。
对于返回const的函数,它是为了防止你或者别人对返回的对象进行写操作(赋值)。
bitwise const成员函数保证了在函数内部任何成员(static除外)都不会被改变,logical const则会给需要改变的成员变量在声明时加上mutable关键字。
第二版条款03:尽量用new和delete而不用malloc和free
在C中,我们谨记malloc和free是我们在使用指针时的忠实朋友,在C++中,我们需要为对象、为含有对象的对象进行内存分配的时候,malloc和free就帮不了我们太多了,因为它们不知道还有构造和析构这样的事情。
为了能够把const与non-const严格区别对待,你需要重载函数,这就意味着代码重复。或许你也想到让non-const去调用const或者反过来(有点危险和莫名其妙,我们不是为了const而来的吗?)。
class TextBlock { public: ... const char& operator[](std::size_t position) const // same as before
{ ... // do bounds checking
... // log access data
... // verify data integrity
return text[position];
}
char& operator[](std::size_t position) // now just calls const op[]
{
return
const_cast<char&>(
// cast away const on 这儿很好理解
static_cast<const TextBlock&>(*this) // add const to *this's type; 避免递归调用自身 [position] // call const version of op[]
);
}
... };
条款04:确定对象在使用前已先被初始化
关于语言本身对初始化的保证,从C到C++一直是个讨论的热点,Scott说:“always initialize your objects before you use them”。
class PhoneNumber { ... };
class ABEntry
{ // ABEntry = "Address Book Entry"
public:
ABEntry(const std::string& name, const std::string& address,
const std::list<PhoneNumber>& phones);
private:
std::string theName;
std::string theAddress;
std::list<PhoneNumber> thePhones;
int num TimesConsulted; };
// 赋值做法,相信每一个C程序员都对这样的写法倍感亲切
ABEntry::ABEntry(const std::string& name, const std::string& address,
const std::list<PhoneNumber>& phones)
{ theName = name; // these are all assignments,
theAddress = address; // not initializations
thePhones = phones
numTimesConsulted = 0; }
// 成员初始列做法,这才是初始化
ABEntry::ABEntry(const std::string& name, const std::string& address,
const std::list<PhoneNumber>& phones) : theName(name),
theAddress(address), // these are now all initializations
thePhones(phones),
numTimesConsulted(0) // 这个built-in type无所谓,不存在效率上的问题 {}
// the ctor body is now empty
不仅仅是习惯问题,先调用default constructor再进行copy assignment显然比直接调用copy constructor要慢半拍(岂止啊)!对于const或references因为无法赋值,只能使用初始列。而且,初始化顺序也应该遵循先base后derived,FCFS的原则。
至于例外也不是没有,为了避免涉及数据读写的大量变量在赋值前多余的初始化(前提是你正确的知道那是多余的),合理改用赋值是明智的。
“The relative order of initialization of non-local static objects defined in different translation units is undefined.”对于different translation units中定义的non-local static objects们,我们不可能也没有必要在每次使用时都对其初始化,然而我们又的确需要初始化已经实施过这样的保证,GoF提出的Singleton模式、解决了这个问题。
说句实话,我很少使用non-local static objects,但是我也很少使用local static objects。虽然我对《Design Pattern》看过多遍,但你也体会到了,那又能说明什么呢?
第二版条款04:尽量使用c++风格的注释
为了强调要从C转向C++,Scott连C的注释风格也一起批判:
if ( a > b ) {
// int temp = a; // swap a and b
// a = b;
// b = temp; } ==>
if ( a > b ) {
/* int temp = a; /* swap a and b */
a = b;
b = temp; */ }
啊喔,因为注释不能嵌套,上面的修改导致了编译时无法通过。
看来,从C到C++,必须要转变的彻底才行。