C++ 空类中有哪些成员函数

1.C++空类中成员函数

空类默认有6个默认函数,分别为:

class Empty{
public:
	Empty(); 							// 缺省构造函数
	Empty( const Empty& ); 				// 拷贝构造函数
	~Empty(); 							// 析构函数
	Empty& operator=( const Empty& ); 	// 赋值运算符
	Empty* operator&(); 				// 取址运算符
	const Empty* operator&() const; 	// 取址运算符 const
};

C++的这些默认函数,只有类在实例化的时候才会被创建,接下来我们分别介绍一下这六个成员函数

1.1 缺省构造函数

  • 是一种特殊的类成员函数,当创建一个类对象时,调用构造函数对类的数据成员进行初始化和分配内存;

  • 构造函数的命名需和类名完全相同;

  • 构造函数可以被重载,可以多个,可以带参数;跟析构函数不同,析构函数只能有一个,不能被重载,不带参数;

1.2 缺省拷贝构造函数

  • 函数名和类型名一样,有两种原型:
    • 参数为地址参数,为了防止无限构造,行成死循环;
    • const目的是常引用,不能改变里面的值;
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<
  • 拷贝构造函数参数为引用,系统自动生成的拷贝构造函数功能简单,只是将arr1的数组首地址直接赋值给arr2的数组首地址,也即num = a.num;这必然导致两个对象指向同一块内存。既然问题出在系统自动生成的拷贝构造函数上,自然要从拷贝构造函数上下手了。下面我们将正确的程序展示
#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

通常,如果一个类中包含指向动态分配存储空间的指针类型的成员变量时,就应该为这个类设计一个拷贝构造函数,除了需要设计一个拷贝构造函数之外,还需要为它添加一个赋值操作符重载函数。

  • 如果不想让对象发生拷贝行为,可以声明一个拷贝构造函数,并将其设置为private属性。

1.3 缺省析构函数

析构函数与构造函数相对应,是对象销毁时自动调用的,主要特点如下:

  • 析构函数只能有一个,不能重载;
  • 析构函数不能有参数;
  • 在主函数中,析构函数在return语句之前执行;

1.4 缺省赋值运算符

  • 拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,赋值运算符是对于一个已经初始化的对象来进行赋值操作。

    A a;
    A b=a;  //调用拷贝构造函数(b不存在)
    A c(a) ;  //调用拷贝构造函数
     
    /****/
     
    class A;
    A a;
    A b;  
    b = a ;  //调用赋值函数(b存在)
    
    
  • 数据成员包含指针对象时,需考虑两种不同的处理需求:一种是复制指针对象,另一种是引用指针对象。拷贝构造函数大多数情况下是复制,而赋值函数是引用对象

  • 实现不一样。拷贝构造函数首先是一个构造函数,它调用时候是通过参数的对象初始化产生一个对象。赋值函数是把一个新的对象赋值给一个原有的对象,所以如果原来的对象中有内存分配先要把内存释放掉,而且要检查一下两个对象是否为同一个对象,如果是不做任何操作,直接返回。

  • 如果不想写拷贝构造函数和赋值函数,又不允许别人使用编译器生成的缺省函数,最简单的办法是将拷贝构造函数和赋值函数声明为私有函数。

  • 对象不存在,没有用别的对象初始化,就是调用了构造函数;

  • 对象不存在,用别的对象初始化,就是用了拷贝构造函数;

  • 对象存在,用别的对象来给它赋值,就是赋值函数;

1.5 缺省取址运算符

1.6 缺省const取址运算符

你可能感兴趣的:(C++,c++,开发语言)