1.体验构造函数
C ++里的类有默认构造函数,自己定义的构造函数,允许通过类构造函数实现类的类型的隐式转换或者显示转换(构造函数前带explicit关键字),比如如下代码Jungle定义了一个类A,定义了两个构造函数:
using namespace std;
class A
{
public:
A()
{
index++;
age = -1;
Num = -1;
cout<<"A()默认构造函数0"<<endl;
}
A(int iAge,int iNum = 0)
{
index++;
age = iAge;
Num = iNum;
cout<<"A()默认构造函数1"<<endl;
}
~A()
{
}
void print()
{
cout<<"Index="<<index<<" Value:\t age = "<<age<<", Num = "<<Num<<endl;
}
///用于A创建的对象计数
static int index;
private:
int age;
int Num;
};
int A::index = 0;
int _tmain(int argc, _TCHAR* argv[])
{
///默认无参构造函数
A x1;
x1.print();
///默认带参构造函数
A x2(100);
x2.print();
///默认带参构造函数
A x3 = A(29,3);
x3.print();
///默认带参构造函数
A x4(10);
x4.print();
///隐式类型转换:将int型转换为A类型,因为定义了带默认参数的构造函数1
A x5 = 123;
x5.print();
A x6 = x5;
getchar();
return 0;
}
main函数里通过不同的初始化方式来调用A的构造函数,并且打印各个对象的值,运行结果如下:
可以看到,不同的初始化方式会调用不同的构造函数,类的静态成员变量index计数对象的个数。
但是奇怪的是x6并没有调用构造函数?
接下来我们把x6的值打印出来看看:
int _tmain(int argc, _TCHAR* argv[])
{
///默认无参构造函数
A x1;
x1.print();
///默认带参构造函数
A x2(100);
x2.print();
///默认带参构造函数
A x3 = A(29,3);
x3.print();
///默认带参构造函数
A x4(10);
x4.print();
///隐式类型转换:将int型转换为A类型,因为定义了带默认参数的构造函数1
A x5 = 123;
x5.print();
A x6 = x5;
x6.print();
getchar();
return 0;
}
不仅成员变量的值与x5相同,连计数index都相同!!!
但明显x5和x6是两个对象。那么为什么x5没有调用上述两个构造函数(甚至是默认构造函数呢?)这就是调用了复制构造函数!其原型为:
A(const A& iA)
2.复制构造函数
- 什么时候会调用复制构造函数?
- 当新建一个对象,并将其初始化为同类已有对象时,将会调用复制构造函数。下列情况都会调用复制构造函数:
A x5 = 123;
x5.print();
//x5是已有的对象
A x6 = x5;
A x7(x5);
A x8 = A(x5);
A *x9 = new A(x5);
- 复制构造函数的“复制”机制是?
- 打印x5与x6的成员变量的值来看,复制构造函数是将已有对象(x5)里的非静态成员变量的值逐个复制,成为浅复制
- 浅复制会不会有问题?
- 对于有指针的情况,会出现很大的问题。比如我们修改类A如下—增加一个成员变量char指针,并在构造函数里用new分配内存,在析构函数里用delete释放:
class A
{
public:
A()
{
index++;
age = -1;
Num = -1;
len = 0;
pt = new char[1];
pt = "\0";
cout<<"A()默认构造函数0"<<endl;
}
A(int iAge,int iNum, char* s)
{
index++;
age = iAge;
Num = iNum;
len = strlen(s);
pt = new char[len +1];
strcpy(pt, s);
cout<<"A()默认构造函数1"<<endl;
}
~A()
{
--index;
delete []pt;
cout<<"析构函数"<<endl;
}
void print()
{
cout<<"Index="<<index<<" Value:\t age = "<<age<<", Num = "<<Num<<" 字符串值:"<<pt<<endl;
}
///用于A创建的对象计数
static int index;
private:
int age;
int Num;
char *pt;
int len;
};
int A::index = 4;
int _tmain(int argc, _TCHAR* argv[])
{
A x5 = A(123,10,"Jungle");
x5.print();
{
A x6 = x5;
x5.print();
}///代码块结束,x6将被析构
A x7(x5);
x5.print();
A x8 = A(x5);
x5.print();
A *x9 = new A(x5);
x5.print();
getchar();
return 0;
}
运行结果如下:
可以看到,打印结果完全不像想象中那样了,甚至出现了乱码!!
分析如下:
A x6=x5; 这一行代码将调用复制构造函数,采用浅复制,包括复制指针。但是指针只是x5中字符串”Jungle”的地址,而不是内容!!,因此x6对象的成员变量x6.pt和x5.pt指向同一个地址。在x6 代码块结束(大括号结束)时,x6对象将被释放,pt指向的地址被delete了,因此x5里的字符串”Jungle”也不在内存中了!至于后面的代码,更是不能运行了,所以出现了乱码。
- 如何解决上述情况呢?
- 用户自己定义复制构造函数
A(const A& iA)
{
index++;
age = iA.age;
Num = iA.Num;
len = strlen(iA.pt);
pt = new char[len+1];
std::strcpy(pt,iA.pt);
cout<<"A()复制构造函数2"<<endl;
}
接下来运行,就不会出错了: