#include <iostream>
using namespace std;
class CSample
{
char ch1, ch2;
public:
friend void set(CSample & s, char c1, char c2);
CSample(char a, char b)
{
ch1 = a;
ch2 = b;
cout<<"CSample Constructor"<<endl;
}
CSample(const CSample & rhs)
{
ch1 = rhs.ch1;
ch2 = rhs.ch2;
cout<<"CSample Copy-constructor"<<endl;
}
CSample & operator = (const CSample & rhs)
{
ch1 = rhs.ch1;
ch2 = rhs.ch2;
cout<<"CSample Operator="<<endl;
return *this;
}
~CSample()
{
cout<<"ch1="<<ch1<<",ch2="<<ch2<<endl;
}
};
void set(CSample & s, char c1, char c2)
{
s.ch1 = c1;
s.ch2 = c2;
}
CSample fun(CSample obj)
{
set(obj, '7', '9');
return obj;
}
int main()
{
CSample obj1('7', '8');
CSample obj2 = obj1;
obj2 = fun(obj1);
return 1;
}
这道题目的输入结果是:
CSample Constructor
CSample Copy-constructor
CSample Copy-constructor
CSample Copy-constructor
CSample Operator=
ch1=7,ch2=9
ch1=7,ch2=9
ch1=7,ch2=9
ch1=7,ch2=8
用的是g++编译的。
解释一下:
obj(‘7’,‘8’)这句话调用一般构造函数,输出CSample Contrucutor
第二句话,用obj1给obj2初始化,用一个已知的对方给另外一个对象初始化,调用拷贝构造函数,所以输出CSample Copy-constructor
调用fun(obj1)时,构造一个形参,所以调用拷贝构造函数,输出CSample Copy-constructor,此时obj1的值为(‘7’,8‘),而这个形参的值为(’7‘,’9‘),这里是按值传参数,所以对obj1没有改变,返回obj的时候,又构造一个临时对象,所以调用拷贝构造函数,输出CSample Copy-constructor,此时这个临时对象的值为(7,9),然后用这个临时对象给obj2赋值,调用重载函数,输出CSample Operator=,
然后反序调用析构函数,先析构临时对象,输出ch1=7,ch2=9;
再析构形参,输出ch1=7,ch2=9;
再析构obj2,输出ch1=7,ch2=9
再析构obj1,输出ch1=7,ch2=8
整个过程结束,要记住,一个对象作为实参进入一个函数时,只是自己拷贝一份进去,该函数结束时要把这份临时的复制对象析构掉,函数返回时返回一个对象也是先构造一个临时对象。
友元函数set并不是类的成员函数,记住。
总结一下构造和析构:
每个类至少有三个构造函数和一个析构函数。
三个构造函数为:普通构造函数,拷贝构造函数,operator=构造函数,如果没有申明构造函数,编译器会自动的生成默认构造函数,但是如果一旦出现了普通构造函数,编译器就不会自动生成默认构造函数。当用一个对象去初始化另外一个对象时,写成(obj) 或者 = obj,调用拷贝构造函数,没有拷贝构造函数,系统会自动生成一个默认的拷贝构造函数,当用一个对象给一个一个存在的对象赋值时,调用operator=构造函数,默认的构造函数,都是通过位拷贝进行复制的,所以,当出现指针的时候,调用默认的构造函数往往会出错。
构造派生类子对象的时候,先调用基类构造函数,按照继承的顺序,在调用子对象的构造函数,按照子对象声明的顺序,在调用自己的构造函数。这个顺序和初始化列表的顺序无关。
对于初始化列表,const成员只能在初始化列表里面进行初始化,static成员只能通过::进行初始化,不能那个写在构造函数里面。
一个对象作为形参的时候,其实是调用了一个默认拷贝构造函数构造了一个形参对象,对象作为返回值的时候也是如此,调用了默认的拷贝构造函数,
析构的顺序和构造的顺序完全相反;
析构函数只有一个,在对象生成周期结束的时候,编译器自动调用析构函数。