先看一段代码,摘自知乎:
#include <iostream>
#include <cmath>
using namespace std;
class Point{
public:
Point(int xx=0,int yy=0){
x=xx;
y=yy;
cout << "Calling constructor of Point" << endl;
}
Point(Point & p);
int getx() {return x;}
int gety() {return y;}
private:
int x,y;
};
Point::Point(Point & p){
x=p.x;
y=p.y;
cout<<"Calling the copy constructor of Point"<<endl;
}
class Line{
public:
Line(Point xp1,Point xp2);
Line(Line & l);
double getLen() {return len;}
private:
Point p1,p2;
double len;
};
Line::Line(Point xp1,Point xp2):p1(xp1),p2(xp2){
cout<<"Calling constructor of Line"<<endl;
double x=static_cast<double>(p1.getx()-p2.getx());
double y=static_cast<double>(p1.gety()-p2.gety());
len=sqrt(x*x+y*y);
}
Line::Line(Line & l):p1(l.p1),p2(l.p2){
cout<<"Calling the copy constructor of Line"<<endl;
len=l.len;
}
int main(){
Point myp1(1,1),myp2(4,5);
cout << endl;
Line line(myp1,myp2);
return 0;
}
输出结果为:
Calling constructor of Point
Calling constructor of Point
Calling the copy constructor of Point
Calling the copy constructor of Point
Calling the copy constructor of Point
Calling the copy constructor of Point
Calling constructor of Line
解释Line line(myp1 myp2)
一句:
Line::Line(Point xp1,Point xp2):p1(xp1),p2(xp2){
cout<<"Calling constructor of Line"<<endl;
double x=static_cast<double>(p1.getx()-p2.getx());
double y=static_cast<double>(p1.gety()-p2.gety());
len=sqrt(x*x+y*y);
}
调用构造函数Line::Line
时,由于采用传值的方式,需要生成实参myp1
,myp2
的副本,可以理解为初始化Point xp1(myp1)
, Point xp2(myp2)
;于是调用了两次拷贝构造函数,同理,初始化列表中p1(xp1), p2(xp2)也调用了两次拷贝构造函数。综上,调用4次拷贝构造函数(都在进入函数体之前完成)。
如果将构造函数Line:Line
改为:
Line::Line(Point xp1,Point xp2){
p1 = xp1; p2 = xp2; // 不用初始化列表的方式
cout<<"Calling constructor of Line"<<endl;
double x=static_cast<double>(p1.getx()-p2.getx());
double y=static_cast<double>(p1.gety()-p2.gety());
len=sqrt(x*x+y*y);
}
输出结果为:
Calling constructor of Point
Calling constructor of Point
Calling the copy constructor of Point
Calling constructor of Point
Calling constructor of Point
Calling constructor of Line
少调用了两次拷贝构造函数,转而执行Point 的构造函数,同样也是在进入函数体之前完成。这里并不是p1 = xp1; p2 = xp2
执行的时候再执行的Point
的构造函数,这一行只完成赋值工作;调用Line
的构造函数时,由于没有初始化列表,将通过默认参数的构造函数首先初始化Line
的成员Point p1, p2
, 产生两条输出,与Line::Line
的函数体内p1,p2
被赋值无关!可以检测,删掉赋值的语句,仍会调用两次Point
的构造函数!
补充:
Point p1 = p2;
与
Point p1;
p1 = p2;
是不同的!
第一种写法调用的时拷贝构造函数,相当于Point p1(p2)
,这里的等号并不是赋值操作!;而第二种写法首先用默认的构造函数初始化p1
(需要定义默认的构造函数),再将p2
的值赋值给p1
,所以也不会调用拷贝构造函数。
一般一个类定义了拷贝构造函数,就应该相应的重载赋值运算符,以确保两者含义一致。