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

总结如下:

1.访问为初始化的值会导致不明确的行为,有时仅仅因为读取为初始化的值,就可能让你的 程序崩溃,或者读入一些随机值污染整个程序。

2.对于内置类型的初始化需要自己去手动完成,除此之外的任何东西,初始化的责任在落在构造函数身上,记住:确保每一个构造函数都能将对象的每一个成员初始化

3.对于成员变量的初始化有两种方法

      ① .在构造函数内赋值:

class Test
{
  private:
	  int _x;
public:
	Test(int x)//构造函数
	{
		_x = x;//在构造函数中赋值,不是初始化
	}
};
   ②使用初始化列表:

class Test
{
  private:
	  int _x;
public:
	Test(int x) :_x(x){}//使用参数初始化列表初始化
};
对于这两种初始话有一定的区别,但是推荐使用参数初始化列表,下面我就说说我推荐的理由:

理由1:C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前(自定义类型),所以严格意义来说①中的 方法不是初始化而是赋值

理由2:有时候必须用参数初始化列表初始化成员,因为const修饰的成员的初始化,引用成员的初始化,子类调用父类的构造函数初始化父类成员

理由3:效率问题,虽然如果成员变量是内置类型的话那么和复合类型的效率差异不会太大,但是如果成员变量是内置类型,效率的影响还是很大的

注意:参数初始化列表的顺序是根据你定义变量的顺序进行初始化的。

下面我就理由③举个例子:

class Test
{
  private:
	  int _x;
public:
	Test() :_x(0){}
	Test(int x)
	{
		_x = x;
	}
	Test(const Test& s)
	{
		_x = s._x;
	}
	Test& operator=(const Test& s)
	{
		_x = s._x;
		return *this;
	}
};
class Testrace
{
private:
	Test _s;
public:
	Testrace(const Test &s)//在这里先调用_s的无参构造函数,然后再对_s进行赋值,调用赋值运算符重载函数
	{
		_s = s;
	}
};
int  main()
{
	Test s1;
	Testrace s2(s1);
	system("pause");
	return 0;
}

这样就会在初始话_s的时候首先调用_s的无参构造函数,然后再对_s进行赋值从而调用赋值运算符重载函数。符合理由1的说法,自定义类型的初始化发生在构造函数进入之前先调用他们的无参构造函数初始化他们。但是在这里先调用这个无参构造函数,然后在调用赋值运算符重载就会显得多余,影响效率,所以应该用参数初始化列表,代码如下:

class Test
{
  private:
	  int _x;
public:
	Test() :_x(0){}
	Test(int x)
	{
		_x = x;
	}
	Test(const Test& s)
	{
		_x = s._x;
	}
	Test& operator=(const Test& s)
	{
		_x = s._x;
		return *this;
	}
};
class Testrace
{
private:
	Test _s;
public:
	Testrace(const Test &s) :_s(s){}//直接调用拷贝构造函数进行初始化
};
int  main()
{
	Test s1;
	Testrace s2(s1);
	system("pause");
	return 0;
}
这样使用参数初始化列表的话,就直接调用_s的拷贝构造函数进行初始化了,这样效率就提高了很多。

4.跨编译单元的初始化问题

首先记住:C++对于定义不同编译单元内的non-local static对象的初始化次序并没有明确定义。

下面给出具体的例子

给出头文件部分定义了两个类,hehe.h

using namespace std;
class Test1
{
public:
	Test1();
};
class Test2
{
public:
	Test2(){ cout<<"Test2的构造函数" << endl; }
	void test2(){ cout << "测试函数" << endl; }
};

给出test1.cpp

#include"hehe.h"
extern Test2 t2;

Test1::Test1()
{
	cout << "Test1构造函数" << endl;
	t2.test2();
}
Test1 t1;
int main()
{
	system("pause");
	return 0;
}

给出test2.cpp

#include"hehe.h"
Test2 t2;

测试结果如下:


会发现在t2对象并未初始化的时候调用了t2对象的 方法,这个函数没有做什么事情所以程序正常,如果函数访问t2的成员的话,问题就大了。怎么解决这种垮文件初始化次序的问题呢?

方法是:将每个non-local static对象搬到自己的专属函数种(该对象在函数种被声明为static,函数返回一个指向对象的引用,然后用户调用这些函数,而不直接设计这些对象。

所以将test2.cpp改为

Test2& GetT2()
{
	static Test2 t2;
	return t2;
}
将test1.cpp改为

extern Test2 t2;
extern Test2& GetT2();
Test1& GetT1()
{
	static Test1 t1;
	return t1;
}
Test1::Test1()
{
	cout << "Test1 构造函数" << endl;
	GetT2().test2();
}
Test1 t1=GetT1();


在想要得到t1或者t2对象的时候直接他们相应的函数就可以了,因为static对象会在函数被调用期间首次遇上该对象定义式的时候被初始化的,所以这就避免了跨文件初始化次序的问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值