学习C++类,首先要说C中的结构体,虽然C++类扩展了C中的结构体,可以添加成员函数,但他们是有区别的。在结构体中,成员变量、成员函数都是公有的,而类中,一般是成员变量是私有的,成员函数是公有的,私有的成员变量一般对象不可调用(数据隐藏),只能是成员函数来调用。
代码1:
1 #include <iostream> 2 using namespace std; 3 4 class MyClass 5 { 6 private: 7 int m_value; 8 public: 9 int get(); 10 void set(int n); 11 }; 12 13 int MyClass::get() 14 { 15 return m_value; 16 } 17 18 void MyClass::set(int n) 19 { 20 m_value=n; 21 } 22 23 int main() 24 { 25 MyClass myclass; 26 //cout<<myclass.m_value<<endl; 27 cout<<myclass.get()<<endl; 28 29 myclass.set(4); 30 cout<<myclass.get()<<endl; 31 return 0; 32 }
如果没有注释掉26行,编译时会出现error C2248: 'm_value' : cannot access private member declared in class 'MyClass',即应证了类中的私有成员变量只能通过公有成员函数调用,对象是不能直接获取的。
执行27行后,显示的是一个不确定的负数,其实,执行25行的MyClass myclass;后编译系统为对象myclass的数据成员(m_value)分配内存空间,并调用默认构造函数MyClass( )自动地初始化对象myclass的m_value值设置为一不确定的值。当然,我们也可以自己设计构造函数。
构造函数主要可以分为如下几种形式:
无参数构造函数 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做。只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来;
一般构造函数(也称重载构造函数) 一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)。 创建对象时根据传入的参数不同调用不同的构造函数;
复制构造函数(也称为拷贝构造函数) 复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中.若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询 有关 “浅拷贝” 、“深拷贝”的文章论述.
代码2
1 #include <iostream> 2 using namespace std; 3 4 class MyClass 5 { 6 private: 7 int m_value; 8 public: 9 MyClass(){m_value=0;} //无参构造函数 10 MyClass(int n){m_value=n;} //一般构造函数 11 MyClass(const MyClass &my){m_value=my.m_value;} //复制构造函数 12 int get(); 13 void set(int n); 14 15 }; 16 17 int MyClass::get() 18 { 19 return m_value; 20 } 21 22 void MyClass::set(int n) 23 { 24 m_value=n; 25 } 26 27 int main() 28 { 29 MyClass myclass1; 30 cout<<myclass1.get()<<endl; 31 32 MyClass myclass2(4); 33 //MyClass myclass2=MyClass(4); 34 cout<<myclass2.get()<<endl; 35 36 MyClass myclass; 37 MyClass myclass3(myclass); 38 cout<<myclass3.get()<<endl; 39 40 return 0; 41 }
代码2分别定义了无参构造函数、一般构造函数和复制构造函数。在主函数中,29行调用了无参构造函数,32行调用了一般构造函数,且它的作用等价注释掉的33行,37行调用了复制构造函数,即通过对象myclass来创建对象myclass3。
其实对于无参构造函数、一般构造函数还有另一种形式:
queue(int qs):qsize(qs),items(0),front(NULL),rear(NULL){}
即由冒号和逗号分隔的初始化列表,被放在构造函数参数的右括号后,函数体的左括号之前。
代码3
1 #include <iostream> 2 using namespace std; 3 4 class MyClass 5 { 6 private: 7 int m_value; 8 public: 9 MyClass(int n){m_value=n;} 10 int get(); 11 void set(int n); 12 void show(); 13 }; 14 15 int MyClass::get() 16 { 17 return m_value; 18 } 19 20 void MyClass::set(int n) 21 { 22 m_value=n; 23 } 24 25 void MyClass::show() 26 { 27 cout<<m_value<<endl; 28 } 29 30 int main() 31 { 32 MyClass myclass1(5); 33 myclass1.show(); 34 35 MyClass myclass2=myclass1; 36 myclass2.show(); 37 38 return 0; 39 }
在代码3中,32行调用自定义的构造函数创建对象myclass1,而35行,却是调用的系统默认的复制构造函数,即系统为对象myclass2 分配了内存并完成了与对象myclass1 的复制过程.编译系统的机制是,如果系统类没有自己定义复制构造函数,会采用默认的,如果自己定义了,则会用定义的,其实代码3和代码4(自定义)的作用和效果是相同的。
代码4
1 #include <iostream> 2 using namespace std; 3 4 class MyClass 5 { 6 private: 7 int m_value; 8 public: 9 MyClass(int n){m_value=n;} 10 MyClass(const MyClass &my){m_value=my.m_value;} 11 int get(); 12 void set(int n); 13 void show(); 14 }; 15 16 int MyClass::get() 17 { 18 return m_value; 19 } 20 21 void MyClass::set(int n) 22 { 23 m_value=n; 24 } 25 26 void MyClass::show() 27 { 28 cout<<m_value<<endl; 29 } 30 31 int main() 32 { 33 MyClass myclass1(5); 34 myclass1.show(); 35 36 MyClass myclass2=myclass1; 37 myclass2.show(); 38 39 return 0; 40 }
代码4中10行就是我们自定义的复制构造函数,复制构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量,且没有其他参数或其他参数都有默认值。
复制构造函数用于将一个对象复制到新创建的对象中,也就是说,它用于初始化过程中(包括按值传递参数),而不是常规的赋值过程中。
那么什么时候需要一般的构造函数,什么时候需要复制构造函数:总的来说,以下三种情况需要复制构造函数,具体:
1. 对象以值传递的方式传入函数参数
2. 对象以值传递的方式从函数返回
3. 对象需要通过另外一个对象进行初始化
其实以上代码中的复制构造函数就是所谓的浅拷贝,即只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员或静态成员,那么浅拷贝就会出问题。