1 #include <iostream>
2 using namespace std;
3
4 class A
5 {
6 private:
7 int value;
8
9 public:
10 A(int n)
11 {
12 value = n;
13 cout << "constructor with argument" << endl;
14 }
15
16 A(const A& other)
17 {
18 value = other.value;
19 cout << "copy constructor" << endl;
20 }
21
22 A& operator=(const A& other)
23 {
24 cout << "assignment operator" << endl;
25 value = other.value;
26 return *this;
27 }
28
29 void func(A other)
30 {
31 }
32 };
33
34 int main()
35 {
36 A a = 10; // constructor with argument
37 A b = 5; // constructor with argument
38 b = a; // assignment operator
39 A c = a; // copy constructor
40 b.func(a); // copy constructor
41
42 return 0;
43 }
程序的运行结果如下:
对于上述程序的第39行,构造c,实质上是c.A(a)。假如拷贝构造函数参数不是引用类型的话,那么将使得c.A(a)变成a传值给c.A(A other),即A other = a,而other没有被初始化,所以other = a将继续调用拷贝构造函数(这也是为什么调用func函数会调用拷贝构造函数)。接下来是构造other,也就是other.A(a),即A other = a,又会触发拷贝构造函数,这样永远地递归下去。
关于上述程序还有一些值得说明的:
1)当某对象没被初始化,这时运用赋值运算符调用的是拷贝构造函数;否则调用的是赋值运算符重载函数;
2)上述程序的“A a = 10”实际存在隐式类型转换(从int到A),这是编译器默认帮我们处理的。这条语句相当于:
A tmp(10);
A a(tmp); // or a = tmp
如果我们不想编译器默认帮我们做这种转换,我们可以在构造函数前加上关键字explicit:
explicit A(int n)
{...}
这样,类似这样的赋值“A a = 10”将通不过编译。
另外,当我们定义如下的函数:如果构造函数前没有加explicit,则我们这样子调用test(10)是可以的;但如果加了explicit就会提示无法从‘int’转换为‘A’。
void test(A other)
{
}
关于explicit可参考:
What does the explicit keyword in C++ mean?
C++ explicit关键字 详解(用于构造函数)
3)关于public、protected、private几个关键字的重新理解。如果我们直接在main函数用a.value是不行的(因为权限是private),但在拷贝构造函数和重载赋值运算符函数中确是可以的,而且,当我们不将传入参数设定为const的话,我们在函数中还可以修改传入参数的值,这不是自相矛盾了吗?具体解释可以参考博文C++ 类访问控制public/private/protected探讨的说法:
“
类是将数据成员和进行于其上的一系列操作(成员函数)封装在一起。注意:成员函数可以操作数据成员(可以称类中的数据成员为泛数据成员)!
对象是类的实例化,怎样理解实例化?其实每一个实例对象都只是对其中的数据成员初始化,内存映像中每个对象仅仅保留属于自己的那份数据成员副本。而成员函数对于整个类而言却是共享的,即一个类只保留一份成员函数。
那么每个对象怎样和这些可以认为是“分离”的成员函数发生联系,即成员函数如何操作对象的数据成员?记住this指针,无论对象通过(.)操作或者(->)操作调用成员函数。编译时刻,编译器都会将这种调用转换成我们常见的全局函数的形式,并且多出一个参数(一般这个参数放在第一个,有点像python中类中函数声明中的self参数),然后将this指针传入这个参数。于是就完成了对象与成员函数的绑定(或联系)。
实例化后就得到同一个类的多个不同的对象,既然成员函数共享的,那么成员函数就可以操作对象的数据成员。
问题是现在有多个对象,成员函数需要知道操作的是哪个对象的数据成员?比如有对象obj1和obj2,都属于A类,A类有public成员函数foo()。如果obj1调用该函数,编译时会给foo函数传入this指针,obj1.foo中操作obj1自身的成员就不用任何修饰,直接访问,因为其中的数据成员自动根据this指针找到。
如果obj1调用该函数,同样可以访问同类的其他对象的数据成员!那么你需要做的是让foo函数知道是同类对象中哪个对象的数据成员,一个解决办法是传入同类其他对象的指针或引用,那么就可以操作同类其他对象的数据成员。
foo(A& obj)
这样定义,然后调用:
obj1.foo(obj2)
就可以在obj1访问obj2的数据成员,而无论这些数据成员是private还是protected
"