1. 构造函数
(1). 构造函数是一种用于创建对象的特殊成员函数,当创建对象时,系统自动调用构造函数。
(2). 构造函数名与类名相同,一个类可以拥有多个构造函数(重载),构造函数可以有任意类型的参数,但不能具有返回类型,连Void也不可以,它有隐含的返回值,该值由系统内部使用。
(3). 构造函数的作用是:为对象分配空间;对数据成员赋初值;请求其他资源。
(4). 如果一个类没有定义构造函数,编译器会自动生成一个无参的默认构造函数。
2. 析构函数:
(1). 析构函数名字为符号“~”加类名,析构函数没有参数和返回值。
(2). 一个类中只可能定义一个析构函数,所以析构函数不能重载。
(3). 析构函数的作用是进行清除对象,释放内存等。当对象超出其定义范围时(即释放该对象时),编译器自动调用析构函数。
在以下情况下,析构函数也会被自动调用:
a. 如果一个对象被定义在一个函数体内,则当这个函数结束时,该对象的析构函数被自动调用。
b. 若一个对象是使用new运算符动态创建的,在使用delete运算符释放它时,delete将会自动调用析构函数。用new创建的对象,必须用delete销毁。
(4). delete释放new产生的空间,而delete [] 释放new []产生的数组空间。
例如:
class A
{
A(){ m_pstr = new char[10];}
~A(){ delete[] m_pstr;m_pstr = NULL;}
}
A *pObj = new A[10];
3. 拷贝构造函数:
(1). 当构造函数的参数为自身类的引用时,这个构造函数称为拷贝构造函数。拷贝构造函数的功能是用一个已有对象初始化一个正在建立的同类对象。
(2). 拷贝构造函数的特点如下:
a. 该函数名与类同名,因为它也是一种构造函数,并且该函数也不被指定返回类型;
b. 该函数只有一个参数,并且是对某个对象的引用;
c. 每个类都必须有一个拷贝构造函数;
d. 如果程序员没有显式地定义一个拷贝构造函数,那么,C++编译器会自动生成一个缺省的拷贝构造函数.
e. 拷贝构造函数的目的是建立一个新的对象实体,所以,一定要保有证新创建的对象有着独立的内存空间,而不是与先前的对象共用。
在定义一些类时,有时需要(而且强立推荐)显式地定义拷贝构造函数。
(3).拷贝构造函数主要在如下三种情况中起初始化作用:
a. 声明语句中用一个对象初始化另一个对象(一定是在"声明并初始化对象"时被调用);例如TPoint P2(P1)表示由对象P1初始化P2时,需要调用拷贝构造函数。
b. 将一个对象作为参数按值调用方式(而不是指针)传递给另一个对象时生成对象副本(即 复制构造函数在"对象作为函数参数"时被调用)。当对象作为函数实参传递给函数形参时,如p=f(N),在调用f()函数时,对象N是实参,要用它来初始化被调用函数的形参,这时需要调用拷贝构造函数。
c. 生成一个临时的对象作为函数的返回结果。但对象作为函数返回值时,如return R时,系统将用对象R来初始化一个匿名对象,这时需要调用拷贝构造函数。
(4).拷贝构造函数的执行:
a. 用已有对象初始化创建对象。
b. 当对象作为函数参数时,因为要用实参初始化形参,也要调用拷贝构造函数。
c. 函数返回值为类类型时,情况也类似。
4. 赋值函数:
(1). 赋值操作符则给对象一个新的值,既然是新的值,说明这个对象原来就有值,这也说明,赋值函数只能被已经存在了的对象调用。
(2). 如果不主动编写拷贝构造函数和赋值函数,编译器将以“位拷贝”的方式自动生成缺省的函数。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。
5. 成员初始化的必要性(摘自<<C++程序设计语言(特别版)>>第221页,略作修改).
(该书作者是C++之父Bjarne Stroustrup)
(1). 对于那些初始化与赋值不同的情况,主要有以下两种情况:
a. 没有默认构造函数的类的成员对象;
b.const 成员和引用成员,对成员的初始式(即初始化列表)是必不可少的。例如:
class A {
string name;
Date founded;
A(const string& n, Date fd);
};
class X {
const int i; // const 成员
A a; // 没有默认构造函数的类的成员对象
A& pa; // 引用成员
X(int ii, const string& n, Date d, A& a): i(ii), a(n, d), pa(a) { };
不存在对这些成员做初始化的其它方式,而且,不对这种成员初始化就是错误。
除了以上的两种情况必须采用初始式之外,采用初始式(初始化列表)还有以下两大优点:
a. 这种形式使得初始化过程更加明显;
b. 会带来效率上的优势。
6. 举例:
"构造函数","析构函数","拷贝构造函数"和"赋值函数"的测试程序:
/* 编辑编译环境:Dev-C++ 4.9.9.2 */
#include "stdio.h"
#include "string.h"
#define NAME_MAXLENGTH 20
class Person
{
public:
//int Person(); // error: return type specification for constructor invalid
//void Person();// error: return type specification for constructor invalid
Person()
{
printf("Person::Person() is called !!! /n");
memcpy(name," NO Name",NAME_MAXLENGTH);
age = 0;
sex = 'W';
}
Person(char* n, int a, char s)
{
printf("Person::Person(char* n, int a, char s) is called !!! /n");
memcpy(name,n,NAME_MAXLENGTH);
age = a;
sex = s;
}
Person(char *n, int a) //与前一个构造函数对比,可见,构造函数可以重载
{
printf("Person::Person(char *n, int a) is called !!! /n");
memcpy(name,n,NAME_MAXLENGTH);
age = a;
sex = 'W';
}
void disp()
{
printf("name: %20s, ", name);
printf("age: %4d, ", age);
printf("sex: %2c ", sex);
printf("/n");
}
char* getName()
{
return name;
}
~Person()
{
printf("destory: %s/n", this->getName());
}
//~Person(int a){ };//error: destructors may not have parameters
//error: `Person::~Person()' and `Person::~Person()' cannot be overloaded
private:
char name[NAME_MAXLENGTH];
int age;
char sex; // 'M': men; 'W': women
};
void testFunction()
{
/* 1. 构造函数的测试 */
//Person zh; //error: no matching function for call to `Person::Person()'
Person Wang("Wang",20,'M'); //使用对象, 调用构造函数
//Person &xiaoWang; //error:`xiaoWang' declared as reference but not initialized
//对于引用类型,一定要在定义的时候进行初始化.
Person &LaoWang = Wang; //正确使用引用,既不调用copy构造函数,也不调用赋值函数
Person *Li = new Person("Li",19); //使用指针,通过new操作符调用了构造函数
Person *pStudent = new Person[3]; //调用三次构造函数Person();
Wang.disp();
Li->disp();
for (int i =0;i++;i<3)
{
pStudent[i].disp();
}
/* 2. 拷贝构造函数的测试 */
Person WangWu = Wang; //调用缺省的拷贝构造函数
WangWu.disp();
/* 3. 赋值函数的测试 */
WangWu = *Li; //调用缺省的赋值函数
WangWu.disp(); //输出结果和Li的输出结果相同。
/* 4. 析构函数的测试 */
delete Li; //用new构造的对象必须使用delete进行撤消.
delete [] pStudent; //用new构造的对象必须使用delete进行撤消.
}
int main()
{
testFunction();
while(1);
}