看看C++构造函数

这也是《C++primer》的笔记和心得,其实对于C++的构造函数很熟悉了,但常常感觉这么用,但都没有深究其中的原理和机制。趁此机会,好好的回顾一下。

构造函数最大的作用就是构造函数并初始化对象,与普通的函数看上去最大的区别就是没有返回值了吧,返回值类型不是void,是没有返回值。还有就是函数名必须与类名相同,最常见的构造函数就是什么参数也不带,其实这个函数也是很重要的(缺省构造函数),因为在定义某些对象的时候必须要用到,因为容器类,比如 vector,要求它们的类元素或者提供缺省的构造函数,或者不提供构造函数。类似地,对于类对象的动态数组,在分配内存的时候也要求或者有缺省构造函数 ,或者没有构造函数 。所以我在写代码的时候除特殊情况外,一般都会自己写一个什么参数都不带的构造函数。

#include <iostream>
using namespace std;

class A
{
public:
	int a = 1;
	A(int a)
	{
		this->a = a;
	}
	A() {}   
};

int main()
{
	A *a = new A[4];  //没有默认构造函数,这个是错误的
	system("pause");
	return 0;
}

 

说到构造函数就不得不说explicit,特地给构造函数私人定制的,用来防止其他类型转换成类的对象。当构造函数只有一个参数,或者有一个没有默认值,而其他参数都有默认值时,可以发生隐式转换,隐式转换虽然方便,但容易带来很多问题,我们也别图那个方便,用explicit来防止这个问题。

class A
{
public:
	int a = 1;
	A(int a)
	{
		this->a = a;
	} 	
};


int main()
{
	A a = 1;	//构造函数没有explicit可以这样,有explicit就会报错
	system("pause");
	return 0;
}

 

 在上面的构造函数名前加上explicit,A a=1就不可以了。同时,注意一点,函数中存在默认值的参数不参与函数签名,就是说默认的参数不作为是否重载的依据,不仅在构造函数中是这样,普通的函数也是。与我们常说的函数的参数个数和参数类型不同就可以构成重载,稍微有点不同,还要注意默认参数问题。

void f(int a, int b = 1)
{

}

void f(int b)
{

}
//后补充的例子
//像这样,写这么写没问题,他不会报错,但一用肯定有问题

类的成员初始化的问题中,除了在函数体中初始化成员,也可以用一种更好的方式:成员初始化列表,这也是构造函数特有的,其他的函数可没有这个骚操作。这构造函数特权挺多的……方式如下:

class A
{
public:
	int a, b, c;
	A(int x, int y, int z):a(x),b(y),c(z)
	{

	} 	
};

说到这里就有一个误区了:就是编译器会自动给所有类生成一个缺省构造函数,用来初始化对象。我刚学的时候也是这么认为的,说是编译器自动插入的,你自己写了就不自动插入了,当时就这么认为,也不会验证。书上关于这方面也是说的特别模糊,只按照例子来说明了一下。看了很多关于这方面的总结,其他大神的看法。一个大佬的博客(不知道这样做有没有侵犯他的合法权益……)http://www.cnblogs.com/coderxiaocai/p/4996085.html其中这么说到:

在类没有显示声明构造函数的情况下,编译器并不总是为我们自动生成默认构造函数,以下4种情况,编译器才会为我们自动生成默认构造函数:

  1.类中有一个类成员含有默认构造函数的,编译器会为该类自动生成默认构造函数,自动插入代码,调用该成员的构造函数;

  2.基类中含有默认构造函数,编译器会为该类自动生成默认构造函数,自动插入代码,调用基类的构造函数;

  3.类中含有虚函数时,由于编译器要为该类生成虚函数表vtable,并为该类的对象生成指向该vtable的vptr,所以需要为该类合成默认构造函数;

  4.虚继承时;

  除了这四种情况,编译器不会为我们自动生成默认构造函数,例如类中的整数、指针、数组等,因为这些成员的初始化对编译器来说都不是必要的,所有都不会自动被初始化,这些成员的初始化需要程序员自己显示编写代码实现;

说的详细,但是没有验证,总让我心里不安稳。我验证完了,好像还真是这样……有兴趣的打个断点,看一下反汇编,有类似这种的:看下面的 call  A::A(……),那就是系统自动给我生成的构造函数。

总结一下吧,上面的几个条件都是不得不用构造函数的,一般来说,写C++的时候几乎都会用到上面的那些,在继承(虚函数不也是用来继承重写的嘛)和用到其他类型的对象时(包括库里面的 string、vector 等等),还有包含const、引用类型的(都是在定义的时候直接初始化了),当然静态的成员不会调用构造函数,因为也不是属于对象的,初始化它干嘛。都会自动生成构造函数,到这里总结的差不多了,不知道那个大佬说的对不对,我总结的有没有偏颇,先这么理解着吧。

构造函数还有量身定制的初始化列表来给对象中的数据初始化,最好使用初始化列表来初始化,对于引用类型的,const类型的以及在继承时调用父类的构造函数,都需要使用初始化列表。


#include <iostream>
using namespace std;

class B
{
public:
	B(int a1)
	{
		a = a1;
	}
	int a;
};

class A:public B
{
public:
	//const类型也必须在这里初始化,同样的属于RAII类型的(资源获取即初始化)应该都会在初始化列表中初始化
	//这里的引用必须用引用来初始化,否则初始化后,temp销毁后对象引用的就是未知的数据了
	//同时,当父类不存在默认构造函数时,必须在子类的初始化列表中调用父类的构造函数用来初始化父类
	//对于默认构造函数,编译器的看法应该是,有 没有默认值的的参数就是不默认构造函数,其他的都应该是默认构造函数
	A(int temp, int &temp1) :B(6),c(temp),d(temp1)  
	{

	}
	int a, b;
	const int c;
	int &d;

};

int main()
{
	int e = 3;
	A a(2,e);
	cout << a.c << endl;
	cout << a.d << endl;
	return 0;
}

 

除了这些构造函数的特性,构造函数还可以是其他类型的,比如private。这样限定构造函数往往是从安全性的角度去考虑的,比如某种情况下不能生成该类型对象,书中是这么说的。

1. 防止用一个类的对象向该类另一个对象作拷贝;
2.指出只有当一个类在继承层次中被用作基类,而不能直接被应用程序操纵时,构造函数才能被调用。(这里我也不大明白)

关于拷贝构造函数,想好好写的就自己写吧……还有重载=运算符来进行对象之间的赋值。以后再写吧,2019/5/11,天气有点热,风不小,烦。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值