一.序
构造函数有三个作用:构造对象、初始化对象、类型转换
二.Attention
1.构造对象
当调动构造函数时,毫无疑问将会有一个新的对象被构造出来
2.初始化对象与类型转换
问题1:变量给对象赋值可取?
#include<iostream>
using namespace std;
class Test
{
public:
Test(int d = 0) : data(d)
{}
private:
int data;
};
void main()
{
Test t;
t = 100;
}
如上,t是实例化出的对象,将一个int型的常量赋给对象,这样正确吗?答案是肯定的!原因:
无论是C,还是C++,不同类型之间赋值,都要进行类型转换,表象是右值给左值赋值,实质上,此时,赋值中间会产生临时对象/临时变量,是临时对象/临时变量在给左值赋值(这一点十分重要!)。
本例中,t = 100实质上做了三步操作
(1)构造出临时对象,假设临时对象为tmp(tmp为Test类型)
(2)tmp.data = 100;
(3)t = tmp;//对象给对象赋值涉及到赋值运算符的重载,后续在赋值运算符的重载函数一文中介绍。
但是请看下列程序:
#include<iostream>
using namespace std;
class Test
{
public:
explicit Test(int d = 0) : data(d)
{
cout<<"Create new object:"<<this<<endl;
}
~Test()
{
cout<<"Free object:"<<this<<endl;
}
/*Test()
{}*/
private:
int data;
};
void main()
{
Test t;
t = 100;//错误 1 error C2679: 二进制“=”: 没有找到接受“int”类型的右操作数的运算符(或没有可接受的转换)
}
为什么会报错?原因是构造函数前加入了explicit
关键字
C++提供了关键字explicit,可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生。声明为explicit的构造函数不能在隐式转换中使用。
简单来说,就是要进行显式转换。于是将22行改成t = (Test)100
即可。这样使得代码的可读性更高,给人的感觉是对100进行强制类型转换,使得它可以用来初始化t.
问题2:如何用对象给变量赋值?
Test t(100);
int value;
value = t;//错误 1 error C2440: “初始化”: 无法从“Test”转换为“int”
要进行上述操作,需要重载int,代码如下:
#include<iostream>
using namespace std;
class Test
{
public:
explicit Test(int d = 0) : data(d)
{
cout<<"Create new object:"<<this<<endl;
}
~Test()
{
cout<<"Free object:"<<this<<endl;
}
public:
operator int()//对int进行重载
{
return data;
}
private:
int data;
};
void main()
{
Test t(100);
int value;
value= t;
}
问题3:构造函数影响着类型转换
是不是所有的类型都能够通过构造函数进行转换呢?不一定
#include<iostream>
using namespace std;
class Test
{
public:
Test()
{
data = 0;
}
~Test()
{
cout<<"Free object:"<<this<<endl;
}
private:
int data;
};
void main()
{
Test t;
t = 100;//错误 1 error C2679: 二进制“=”: 没有找到接受“int”类型的右操作数的运算符(或没有可接受的转换)
}
此时,无法生成中间的临时对象。更何谈将100赋值给中间临时对象tmp的data,更不要说用tmp初始化t了。
将代码进行更改,是可以用100构造出中间临时对象即可:
#include<iostream>
using namespace std;
class Test
{
public:
Test(int d) : data(d)
{
cout<<"Create new object:"<<this<<endl;
}
Test()
{
cout<<"Create new object:"<<this<<endl;
data = 0;
}
~Test()
{
cout<<"Free object:"<<this<<endl;
}
public:
operator int()
{
return data;
}
private:
int data;
};
void main()
{
Test t;
t = 100;
}
可以看到构造函数调动了两次
三.总结
不同类型之间进行赋值,一定会调动构造函数产生中间的临时变量,如果不能产生,就意味着这样的做法是不能编译通过的。