EffectiveC++读书笔记(2)条款4、5、6

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

         内置类型、STL

         对于内置类型,像int、double等,其初始值都是垃圾值。书中建议:永远在使用对象之前先将它初始化。

         而对于STL来说,C++已经为这些容器提供了构造函数,会自动地初始化,我们可以直接使用而可以不初始化。

        

         自定义类

         确保每一个构造函数都将对象的每一个成员初始化

         我们还需要分清初始化与赋值。对于一个类,比如:

class A
{
	private:
		int x;
		double y;
	public:
		A()	
		{
			x = 0;
			y = 0.0;
		}
};

这是赋值。因为初始化发生在调用默认构造函数时,是在进入该构造函数之前,成员变量赋为了垃圾值,而上述方法只是进入构造函数之后新值覆盖旧值。而只有用如下这样的参数列表的形式,才是初始化。

class A
{
	private:
		int x;
		double y;
	public:
		A() : x(0), y(0.0){}

};

就效率上来说,用参数列表的方法效率更高。第一种方法首先调用默认构造函数进行初始化,而后再调用一个构造函数将其赋予新值。而第二种方法直接将参数列表中的各个成员变量作为实参,构造函数本体不必有任何动作。

         此外,如果当成员变量是const或reference,就必须初始化而不能被赋值。

 

         不同编译单元内定义的non-local static 对象

         static对象:生命期从被构造出来知道程序结束(main()结束后自动调用析构函数)。包括:global对象、定义于namespace作用域内的对象、class内、函数内、file作用域内被声明为static的对象。函数内的static对象称为local static对象,其他的成为non-local static对象。

         编译单元:产出单一目标文件的源码

         这个话题讨论的情况是多个文件。

文件1中如下定义:

int x = 1;

文件2中使用该变量:

extern int x;
int y = x * x;

显而易见的是,文件2中使用的x必须依赖于文件1中的初始化,如果文件1的初始化在文件2之后,则文件2使用的就是垃圾值。产生这个问题的原因是:C++对定义于不同编译单元内的non-local static对象的初始化次序并无明确定义。因为决定它们的初始化次序相当困难。所以我们需要用巧妙的设计模式(singleton)避免这种问题:将每个non-local static对象搬到自己的专属函数内,并在该函数中声明为static。

         所以我们可以将文件一修改成一个函数:

int& GetX()
{
	static int x = 1;
	return x;
}

在文件2中调用该函数:

int y = GetX() * GetX();

使用函数返回的“指向static对象”的引用,而不再使用static对象自身,这样在调用函数时已经保证x已经初始化了。

 

 

条款05:了解C++默默编写并调用哪些函数

         一个空类,如果没有声明,编译器会为其声明:

                  默认构造函数

                  拷贝构造函数

                  析构函数

                  赋值运算符重载

         他们都是public的,inline的。

 

对于构造函数和赋值运算符,需要特别分清:

         有一个类A,实例化一个对象a,有三种初始化的方式:

         ①A a( b );

         ②A a = b;

         ③A a;

           a = b;

         方法①,一目了然,调用的是拷贝构造函数

         而方法②,调用的是拷贝构造函数。方法③,调用的是赋值运算符。

虽然②和③都使用了"=",但是区别在于是否在使用"="使产生了新的对象。方法③中在"="之前已经产生了新的对象,所以调用的是赋值运算符。

 

当一个类的成员变量中有const类型和引用类型时,C++会拒绝生成赋值运算符的重载。因为在调用构造函数的时候,已经为他们初始化。如果我们试图调用赋值运算符的重载,会报如下错误:

所以我们需要自己为他们定义重载赋值运算符。

         如果我们不用赋值运算符重载(方法③)的话,而是用拷贝构造函数(方法①或②),那么也是可以的。

 

条款06:若不想使用编译器自动生成的函数,就该明确拒绝

         该条款要说明的问题是:不要让客户代码调用类的拷贝构造函数和赋值函数。但是上一条款说过,这两个函数如果已经定义,就能使用。即使没有定义,编译器也会默默地帮我们定义,所以看似用户代码无论如何都能调用。

         这种问题的解决方法就是自己写拷贝构造函数和赋值函数,而且把它们写在private中,这样就解决了这个问题。

         但是如果遇上友元函数或者友元类呢?它们可以访问private下的成员函数。所以我们需要再进一步的改动:将它们的声明放在一个基类中,一个专门为阻止调用这两个函数的基类。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值