C++拷贝构造函数
1 引入拷贝构造函数
对象赋值语句:就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。
C++支持两种初始化形式:
拷贝初始化 int a = 5; 和直接初始化 int a(5); 对于其他类型没有什么区别,对于类类型直接初始化直接调用实参匹配的构造函数,拷贝初始化总是调用拷贝构造函数,也就是说:
A x(2); //直接初始化,调用构造函数
A y = x; //拷贝初始化,调用拷贝构造函数
必须定义拷贝构造函数的情况:
只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义拷贝构造函数也可以拷贝;有的类有一个数据成员是指针,或者是有成员表示在构造函数中分配的其他资源,这两种情况下都必须定义拷贝构造函数。
2 拷贝构造函数的定义及其作用
拷贝构造函数是一种特殊的构造函数,其形参是本类对象的引用。
(它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。)
其作用是:使用一个已经存在的对象去初始化另一个同类的对象。
3 拷贝构造函数的结构
classname (const classname &obj) {
// 构造函数的主体
}
obj 是一个对象引用,该对象是用于初始化另一个对象的。
4 拷贝构造函数的特点
(1) 因为该函数也是一种构造函数,所以其函数名与类名相同,并且该函数也没有返回值类型。
(2) 该函数一般只有一个参数,并且是同类对象的引用。
(3) 每个类都必须有一个拷贝构造函数。程序员可以根据需要定义特定的拷贝构造函数,以实现同类对象之间数据成员的传递。如果程序员没有定义类的拷贝构造函数,系统就会自动生成产生一个缺省的拷贝构造函数。
例一:
#include <iostream>
using namespace std;
class Line
{
public:
int getLength(void);
Line(int len); // 简单的构造函数
Line(const Line& obj); // 拷贝构造函数
~Line(); // 析构函数
private:
int* ptr;
};
// 成员函数定义,包括构造函数
Line::Line(int len)
{
cout << "调用构造函数" << endl;
// 为指针分配内存
ptr = new int;
*ptr = len;
}
Line::Line(const Line& obj)
{
cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷贝值
}
Line::~Line(void)
{
cout << "释放内存" << endl;
delete ptr;
}
int Line::getLength(void)
{
return *ptr;
}
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() << endl;
}
// 程序的主函数
int main()
{
Line line1(10);
Line line2 = line1; // 这里也调用了拷贝构造函数
display(line1);
display(line2);
return 0;
}
运行结果:
调用构造函数
调用拷贝构造函数并为指针 ptr 分配内存
调用拷贝构造函数并为指针 ptr 分配内存
line 大小 : 10
释放内存
调用拷贝构造函数并为指针 ptr 分配内存
line 大小 : 10
释放内存
释放内存
释放内存
对以上程序稍作修改,通过使用已有的同类型的对象来初始化新创建的对象:
例二:
#include <iostream>
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // 简单的构造函数
Line( const Line &obj); // 拷贝构造函数
~Line(); // 析构函数
private:
int *ptr;
};
// 成员函数定义,包括构造函数
Line::Line(int len)
{
cout << "调用构造函数" << endl;
// 为指针分配内存
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷贝值
}
Line::~Line(void)
{
cout << "释放内存" << endl;
delete ptr;
}
int Line::getLength( void )
{
return *ptr;
}
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}
// 程序的主函数
int main( )
{
Line line1(10);
Line line2 = line1; // 这里也调用了拷贝构造函数
display(line1);
display(line2);
return 0;
}
运行结果:
调用构造函数
调用拷贝构造函数并为指针 ptr 分配内存
调用拷贝构造函数并为指针 ptr 分配内存
line 大小 : 10
释放内存
调用拷贝构造函数并为指针 ptr 分配内存
line 大小 : 10
释放内存
释放内存
释放内存
例三:
#include<iostream>
using namespace std;
class Rect {
public:
Rect(int w=13 , int h=14) //简单构造函数
{
width = w;
height = h;
}
void print();
Rect(const Rect& r);
private:
int width;
int height;
};
Rect::Rect(const Rect& r)
{
width = r.width;
height = r.height;
}
void Rect::print()
{
cout << "width=" << width << " ";
cout << "height = " << height;
cout << endl;
}
int main()
{
Rect A;
cout << "A:";
A.print();
cout << "B copy A: " << endl;
Rect B(A);
cout << "B:";
B.print();
}
5 调用拷贝构造函数的3种情况
在C++中,下面三种情况需要调用拷贝构造函数!
对象需要通过另外一个对象进行初始化;
对象以值传递的方式传入函数参数
对象以值传递的方式从函数返回
5.1对象需要通过另外一个对象进行初始化;
#include<iostream>
using namespace std;
class Point
{
public:
Point(int a = 0, int b = 0)
{
x = a; y = b;
cout << "Using normal constructor\n";
}
Point(const Point& p)
{
x = p.x; y = p.y;
cout << "Using copy constructor\n";
}
void print()
{
cout << x << " " << y << endl;
}
private:
int x, y;
};
int main()
{
Point p1(985, 211);
p1.print();
Point p2(p1);//对象需要通过另外一个对象进行初始化
p2.print();
Point p3 = p1;//对象需要通过另外一个对象进行初始化
p3.print();
return 0;
}
Using normal constructor
985 211
Using copy constructor
985 211
Using copy constructor
985 211
5.2对象以值传递的方式传入函数参数
#include<iostream>
using namespace std;
class Point
{
public:
Point(int a = 0, int b = 0)
{
x = a;
y = b;
cout << "Using normal constructor\n";
}
Point(const Point& p)
{
x = p.x;
y = p.y;
cout << "Using copy constructor\n";
}
void print()
{
cout << x << " " << y << endl;
}
private:
int x, y;
};
void fun1(Point p) {
p.print();
}
int main()
{
Point p1(66,99);
fun1(p1);
return 0;
}
运行结果:
Using normal constructor
Using copy constructor
66 99
5.3对象以值传递的方式从函数返回
#include<iostream>
using namespace std;
class Point
{
public:
Point(int a = 0, int b = 0)
{
x = a;
y = b;
cout << "Using normal constructor\n";
}
Point(const Point& p)
{
x = p.x;
y = p.y;
cout << "Using copy constructor\n";
}
void print()
{
cout << x << " " << y << endl;
}
private:
int x, y;
};
Point fun2() // 类名 方法名
{
Point p4(33, 15);
return p4;
}
int main()
{
Point p; //定义对象
p = fun2(); //对象以值传递的方式从函数返回
p.print();
return 0;
}
思考:为什么出现两次Using normal constructor?
Using normal constructor
Using normal constructor
Using copy constructor
33 15
最后一例子:
#include <iostream>
using namespace std;
class CExample
{
private:
int a;
public:
//构造函数
CExample(int b)
{
a = b;
cout << "creat a= " << a << endl;
}
//拷贝构造
CExample(const CExample& C)
{
a = C.a;
cout << "\nUsing copy constructor\n " << endl;
}
//析构函数
~CExample()
{
cout << "delete: " << a << endl;
}
void Show()
{
cout << "copy a= " << a << endl;
}
};
//全局函数,传入的是对象
void g_Fun(CExample C)
{
C.Show();
}
int main()
{
CExample test(10);
//传入对象
g_Fun(test);
// test.Show();
return 0;
}
提问:如果把去掉 // test.Show();
的注释,结果会如何?为什么?构造函数和析构函数的执行顺序如何?欢迎评论!
图一:
图二: