空类默认有6个默认函数,分别为:
class Empty{
public:
Empty(); // 缺省构造函数
Empty( const Empty& ); // 拷贝构造函数
~Empty(); // 析构函数
Empty& operator=( const Empty& ); // 赋值运算符
Empty* operator&(); // 取址运算符
const Empty* operator&() const; // 取址运算符 const
};
C++的这些默认函数,只有类在实例化的时候才会被创建,接下来我们分别介绍一下这六个成员函数
是一种特殊的类成员函数,当创建一个类对象时,调用构造函数对类的数据成员进行初始化和分配内存;
构造函数的命名需和类名完全相同;
构造函数可以被重载,可以多个,可以带参数;跟析构函数不同,析构函数只能有一个,不能被重载,不带参数;
Empty(Empty &e);
Empty(const Empty &e); // 这种规定在创建新对象的时候不得修改被拷贝的对象
// 如果不是引用,而是通过传值的方式将实参传递给形参,这中间本身就要经历一次对象的拷贝过程,而对象拷贝则必须调用拷贝构造函数,如此一来则会形成一个死循环,无解。所以拷贝构造函数的参数必须是对象的引用。
Empty(const Empty &e, int a = 5);
#include
using namespace std;
class Array
{
public:
Array(){length = 0;num = NULL;}
Array(int *A,int n);
void setnum(int vallue,int index);
int *getaddress();
int getaddress();
void display();
private:
int length;
int *num;
};
Array::Array(int *A,int n)
{
num = new int [n];
length = n;
for (int i = 0;i < n; i++)
num[i] = A[i];
}
void Array::setnum(int value,int index)
{
if(index < length)
num[index] = value;
else
cout<<"index out of range!"<<endl;
}
void Array::display()
{
for(int i = 0;i < length;i++)
cout<<num[i]<<" ";
cout<<endl;
}
int *Arry::getaddress()
{
return num;
}
int main()
{
int A[5] = {1,2,3,4,5};
Array arr1(A,5);
arr1.display();
Array arr2(arr1);
arr2.display();
arr2.setnum(8,2);
arr2.display();
arr1.display();
cout<<arr1.getaddress()<<" "<<arr2.getaddress()<<endl;
return 0;
}
运行结果如下:
1 2 3 4 5
1 2 3 4 5
1 2 8 4 5
1 2 8 4 5
00331F58 00331F58
/*
在本例中,我们重新定义了一个Array类,可以理解为一个整形数组类,这个类中我们定义了两个成员变量:整形指针num和数组长度length。
类中定义了一个默认构造函数,声明了一个带参构造函数。默认构造函数很简单,带参构造函数则是用于将一个已有的数组全部拷贝给类对象。
除了两个构造函数之外,我们还定义四个成员函数,一个是用于修改数组中数值的setnum函数、一个打印数组中所有元素的display函数、一个返回数组首地址的函数getaddress和一个返回数组长度的函数getlength。除了默认构造函数之外和getlength函数之外,所有的函数在类外都有定义。
接下来我们看一下主函数。主函数中,我们先定义了一个数组,包含五个元素,分别是从1到5。之后用Array类创建对象arr1,并且用A数组初始化对象arr1,此时arr1对象相当于拥有一个数组,该数组包含5个元素,打印出来的结果是“1 2 3 4 5 ”,没有问题。之后用arr1对象初始化arr2对象,因为我们在类中没有显示地定义一个拷贝构造函数,因此系统会自动为我们生成一个拷贝构造函数,该拷贝构造函数的定义如下:
Array::Array(Array &a)
{
length = a.length;
num = a.num;
}
通过系统自动生成的拷贝构造函数完成arr2对象的创建,同样的arr2也是有5个元素的数组,打印出来的结果是“1 2 3 4 5 ”,同样没有问题。
之后我们调用成员函数setnum,将arr2对象下标为2的元素修改为8(原先是3)。此时打印arr2中数组元素,结果为“1 2 8 4 5 ”,正确,arr2第三个元素确实被修改掉了。
后我们再调用arr1.display(),奇怪的事情发生了,它的打印结果竟然也是“1 2 8 4 5 ”!我们之前并未修改过第三个元素的值的,这是怎么一回事呢?不急,我们再来看一下最后一句“cout<
#include
using namespace std;
class Array
{
public:
Array(){length = 0;num = NULL;}
Array(int *A,int n);
Array(Array &a);
void setnum(int value,int index);
int *getaddress();
void display();
int getlength(){return length;}
private:
int length;
int *num;
};
Array::Array(Array&a)
{
if(a.num!=NULL)
{
length = a.length;
num = new int [length];
for(int i = 0;i < length;i++)
num[i] = a.num[i];
}
else
{
length = 0;
num = 0;
}
}
Array::Array(int *A,int n)
{
num = new int [n];
length = n;
for(int i = 0;i < n;i++)
num[i] = A[i];
}
void Array::setnum(int value,int index)
{
if(index <length)
num[index] = value;
else
cout<<"index out of range!"<<endl;
}
void Array::display()
{
for (int i = 0;i < length;i++)
cout<<num[i]<<" ";
cout<<endl;
}
int *Array::getaddress()
{
return num;
}
int main()
{
int A[5] = {1,2,3,4,5};
Array arr1(A,5);
arr1.display();
Arry arr2(arr1);
arr2.display();
arr2.setnum(8,2);
arr2.display();
arr1.display();
cout<<arr1.getaddress();" "<<arr2.getaddress()<<endl;
return 0;
}
运行结果如下:
1 2 3 4 5
1 2 3 4 5
1 2 8 4 5
1 2 3 4 5
00311F58 00487268
通常,如果一个类中包含指向动态分配存储空间的指针类型的成员变量时,就应该为这个类设计一个拷贝构造函数,除了需要设计一个拷贝构造函数之外,还需要为它添加一个赋值操作符重载函数。
析构函数与构造函数相对应,是对象销毁时自动调用的,主要特点如下:
拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,赋值运算符是对于一个已经初始化的对象来进行赋值操作。
A a;
A b=a; //调用拷贝构造函数(b不存在)
A c(a) ; //调用拷贝构造函数
/****/
class A;
A a;
A b;
b = a ; //调用赋值函数(b存在)
数据成员包含指针对象时,需考虑两种不同的处理需求:一种是复制指针对象,另一种是引用指针对象。拷贝构造函数大多数情况下是复制,而赋值函数是引用对象
实现不一样。拷贝构造函数首先是一个构造函数,它调用时候是通过参数的对象初始化产生一个对象。赋值函数是把一个新的对象赋值给一个原有的对象,所以如果原来的对象中有内存分配先要把内存释放掉,而且要检查一下两个对象是否为同一个对象,如果是不做任何操作,直接返回。
如果不想写拷贝构造函数和赋值函数,又不允许别人使用编译器生成的缺省函数,最简单的办法是将拷贝构造函数和赋值函数声明为私有函数。
对象不存在,没有用别的对象初始化,就是调用了构造函数;
对象不存在,用别的对象初始化,就是用了拷贝构造函数;
对象存在,用别的对象来给它赋值,就是赋值函数;