构造函数:
定义:构造函数时一个类中与类名相同的特殊成员函数。
特点:构造函数不能有返回值类型声明。
构造函数在创建对象时(初始化,被赋值时不会)被自动调用。
自定义了有参构造函数后,系统不会再自动生成无参构造函数,如果在创建对象时没有显示调用有参构造函数时,系统会自动调用无参构造函数,而此时有没有无参构造函数,会发生错误。
#include <stdio.h> class Test { private: int i; int j; public: int getI() { return i; } int getJ() { return j; } Test() { i = 1; j = 2; } }; Test gt; int main() { printf("gt.i = %d ", gt.getI()); // 1 printf("gt.j = %d ", gt.getJ()); // 2 Test t1; printf("t1.i = %d ", t1.getI()); // 1 printf("t1.j = %d ", t1.getJ()); // 2 Test* pt = new Test; printf("pt->i = %d ", pt->getI()); // 1 printf("pt->j = %d ", pt->getJ()); // 2 delete pt; return 0; }
构造函数和继承:
子类不能继承父类的构造函数.但是在初始化时,为了初始化从父类继承的成员编译器会调用父类的构造函数.
具体规则如下:
- 如果子类没有定义构造方法,则调用父类的无参数构造方法
- 如果子类定义了构造方法,不论无参数还是带参数,在创建子类的对象的时候,首先执行父类无参数的构造方法,然后执行自己的构造方法。
- 在创建子类对象的时候,如果子类的构造函数没有显式调用父类的构造函数,则会调用父类的默认无参构造函数。
- 在创建子类对象时候,如果子类的构造函数没有显式调用父类的构造函数,且父类自己提供了无参构造函数,则会调用父类自己的无参构造函数。
- 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类只定义了自己的有参构造函数,则会出错(如果父类只有有参数的构造方法,则子类必须显示调用此带参构造方法)。
- 如果子类调用父类带参数的构造方法,需要初始化父类成员对象的方法。
对象的定义:申请对象的空间,调用对象的构造函数。
对象的声明:为对象占位。
Test t; // 定义 int main() { extern Test t; // 声明 }
类成员的初始化:
构造函数:一个类中可以有多个重载的构造函数,每个构造函数可以有不同的参数列表,以便不同的初始化类的成员。
class Test { public: int i; Test(){i=0;} Test(int v){i=1;} // 重载的构造函数 }; int main(void) { Test t1; // 定义对象并初始化,根据重载规则调用 Test(){},i自动初始化为0. Test t2(1); // 定义对象并初始化,根据重载规则调用 Test(int v){},用1初始化对象t2的成员i。 Test t3 = 2; // 定义对象并初始化,根据重载规则调用 Test(int v){}, 用2初始化对象t2的成员i。 t1 = t2; // 不会调用构造函数。 }
赋值与初始化:
int i = 100; // 这是初始化, 这是用赋值符号来初始化,分配空间
int i(100); // 这是初始化
i = 100; // 这是赋值,给空间赋值
如何手动调用构造函数:
#include <stdio.h> class Test { private: int m_value; public: Test() { m_value = 0; } Test(int v) { m_value = v; } }; int main() { Test ta[3] = {Test(), Test(1), Test(2)}; // 指定初始化值便可手动调用构造函数。 Test t = Test(100); // 指定对象t成员m_value初始值,手动调用构造函数。 return 0; }
数组类的实现:
提供函数获取数组长度。
提供函数设置/获取数组元素。
#ifndef _INTARRAY_H_ #define _INTARRAY_H_ class IntArray { private: int m_length; int* m_pointer; public: IntArray(int len); int length(); // 获取数组长度。 bool get(int index, int& value); // 获取数组元素。 bool set(int index ,int value); // 设置数组元素。 void free(); // 释放数组空间。 }; #endif
#include "IntArray.h" IntArray::IntArray(int len) { m_pointer = new int[len]; for(int i=0; i<len; i++) { m_pointer[i] = 0; } m_length = len; } int IntArray::length() { return m_length; } bool IntArray::get(int index, int& value) { bool ret = (0 <= index) && (index < length()); if( ret ) { value = m_pointer[index]; } return ret; } bool IntArray::set(int index, int value) { bool ret = (0 <= index) && (index < length()); if( ret ) { m_pointer[index] = value; } return ret; } void IntArray::free() { delete[]m_pointer; }
#include <stdio.h> #include "IntArray.h" int main() { IntArray a(5); for(int i=0; i<a.length(); i++) { a.set(i, i + 1); } for(int i=0; i<a.length(); i++) { int value = 0; if( a.get(i, value) ) { printf("a[%d] = %d ", i, value); } } a.free(); return 0; }
特殊的构造函数:
1. 无参数构造函数
没有参数的构造函数
a. 当但类中没有定义任何构造函数时,编译器自动提供一个无参构造函数,函数体为空。
2. 拷贝构造函数
其参数为: const class_type&
a. 当类中没有定义拷贝构造函数时,编译器自动提供一个拷贝构造函数,简单的进行成员变量的复制(t2=t1)。
#include <stdio.h> class Test { private: int i; int j; public: int getI() { return i; } int getJ() { return j; } /*Test(const Test& t) // 拷贝构造函数 { i = t.i; j = t.j; } Test() // 无参构造函数 { }*/ // 无两个特殊构造函数的是,编译器自动生成构造函数。 }; int main() { Test t1; Test t2 = t1; // 调用了拷贝构造函数 printf("t1.i = %d, t1.j = %d ", t1.getI(), t1.getJ()); printf("t2.i = %d, t2.j = %d ", t2.getI(), t2.getJ()); return 0; }
拷贝构造函数的意义:
用一个已经存在的对象初始化另外一个对象,从而创造出两个相同的对象。
1. 浅拷贝
拷贝物理状态(对象占据内存空间的每个字节是否相同),单纯的复制。
2. 深拷贝
拷贝逻辑状态(对象占据的内存地址是否相同)。
注意:编译器的默认拷贝构造函数时浅拷贝。
#include <stdio.h> class Test { private: int i; int j; int* p; public: int getI() { return i; } int getJ() { return j; } int* getP() { return p; } Test(const Test& t) // 深拷贝 { i = t.i; j = t.j; p = new int; // 指针变量p在拷贝后和t.p指向不同地址,但地址中的内容相同 *p = *t.p;
printf("address of t.p = %p
",t.p); // 0x7fffe56a4e70
printf("address of p = %p
",p); // 0x7fffe56a5ea0
} Test(const Test& t) // 浅拷贝 { i = t.i; j = t.j; p = t.p; // 指针变量P在拷贝后和t.p指向相同地址,地址中的内容也相同
printf("address of t.p = %p
",t.p); // 0x7fffce842e70
printf("address of p = %p
",p); // 0x7fffce842e70
} Test(int v) { i = 1; j = 2; p = new int; *p = v; } void free() { delete p; } }; int main() { Test t1(3); Test t2(t1); // 定义构造函数t2并用 t1去初始化,调用了构造函数 printf("t1.i = %d, t1.j = %d, *t1.p = %d ", t1.getI(), t1.getJ(), *t1.getP()); printf("t2.i = %d, t2.j = %d, *t2.p = %d ", t2.getI(), t2.getJ(), *t2.getP()); t1.free(); t2.free(); // 如果使用了浅拷贝会error 两个P指针指向了相同的空间 return 0; }
什么时候使用深拷贝:
对象成员中指向了系统的资源。
1. 成员指向了动态的内存空间。
2. 成员打开了外存中的文件。
3. 成员使用了系统中的网络。
一般性原则:自定义拷贝函数必须实现深拷贝。