Thinking in C++ ----第8,9,10章(const成员函数、内联函数、静态对象的析构函数调用时机)

时间:2022-08-30 19:34:38

一、const成员函数

1. 编译器认为非const的成员函数会改变对象中的数据成员,因此编译器不允许它被const对象所调用。

2. const在函数定义中被认为是函数标识符的一部分,编译器和链接器都会检查const。因此若要定义一个const成员函数,需要在声明和定义中同时出现const声明。

 

按位(bitwise)const和按成员(memberwise)const

按位const的意思是,对象中的每个字节都是固定不变的。按成员const意思是,虽然整个对象从概念上讲是不变的,但是可以改变某个成员。当声明一个const对象时,它默认是按位const的,有两种有内部const成员函数改变数据成员的方法:

1. 强制转换常量性(casting away constness)

首先,在const成员函数内部,this实际上是一个const指针,想要改变它的某个成员,需先将this转换成普通的指针,例如:

1  class C{
2  private:
3      int i;
4  public:
5      void func() const{
6        ((C*) this)->i++;               //  将this由const C*强制转换为C*
7         const_cast<C*>( this)->i++;     //  C++风格的转换方式
8      }
9 };

 

2. 使用mutable关键字

如果一个数据成员被mutable关键字修饰,那么说明这个成员在const成员函数中可以被修改,如下所示:

1  class C{
2  private:
3     mutable  int i;   //  使用mutable进行修饰
4  public:
5      void func() const{
6        i++;   //  i使用mutable修饰,可以对其进行修改
7      }
8 };

 

只读存储能力

如果一个对象被定义为const对象,它就成为了被放进ROM中的候选者,这经常是嵌入式系统所需要考虑的重要事情,

一个const对象可以被放入ROM中所需具备的条件:

1. 必须是按位const

2. classstruct没有用户定义的构造函数或析构函数;

3. 不能有基类,它的数据成员类型也不能包含用户定义的构造函数或析构函数。

二、内联函数

宏的缺陷

C中,保持效率的一个方法是使用宏(macro),宏看起来像函数,但它不需要函数调用的开销。但是在C++中宏存在三个问题:

1. 宏外观上像函数,但并不是函数,它的某些行为容易导致错误的发生;

2. 宏不允许访问类中的成员;

3. C++中,不能对宏进行访问控制,因为宏从它定义的地方开始有效。

 

内联函数

内联函数本质上是一个函数,它具备函数所具备的一切特征,唯一不同的是,当编译器发现内敛函数在某个地方被调用时,它会将内联函数像宏一样展开。因此内联函数的调用不需要开销。

内联函数的链接方式属于内部链接,内联函数的定义应放在头文件中,在内联函数调用点处,它的定义必须是可见的。内联函数在程序中的定义可能不止一处,但在一个源文件中只能出现一次,而且在所有源文件中其定义必须完全相同。

 

内联函数的定义

1. 对于一般的函数需在声明和定义前加inline关键字

2. 对于类成员函数,在类体内定义的默认的就是内联函数(对于短小的成员函数,一般都定义在类体内),不必使用inline关键字进行修饰;若在类体外定义,只需在定义前加inline关键字就行了,且这个定义必须与包含它的类放在同一个文件中。建议,所有函数定义都放到类外部以保持接口清晰。

 

内联函数的限制

通常情况下,编译器不会为内联函数建立存储空间,但是,如果违反内联函数的规则,编译器就会简单地将内联函数视为普通函数,为其分配存储空间。下面是内联函数的一些规则:

1. 如果函数太复杂,编译器将不执行内联。

2. 如果要显示或隐式地取内联函数的地址,那么编译器也不能执行内联。 

      3. 虚函数不能为内联函数,因为内联函数是在编译期,而虚函数需要在运行期确定。

 

最后,内联仅是对编译器的一个建议,编译器不会被强迫内联任何代码。

三、静态对象的析构函数调用时机

静态对象的析构函数在程序从main()中退出时,或标准C库函数exit()被调用时才会被调用。多数情况下,main()函数也是通过exit()来结束程序的。这意味着析构函数内部使用exit()是很危险的,因为这样将可能导致递归调用(VS2008中做了下试验,并没有发生递归调用,但是静态对象的析构的确是在调用exit的时候发生的)。但如果用标准的C库函数abort()来退出程序,静态对象的析构函数将不会被调用。

可以用标准C库函数atexit()来制定当程序跳出main()时应当执行的操作。在这种情况下,在跳出main()或调用exit()之前,用atexit注册的函数可以在所有对象的析构函数之前被调用。

以后要看的

1. 标准C库函数atexit()

2. 命名空间的使用