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


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

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

    class Empty{
    public:
    	Empty(); 							// 缺省构造函数
    	Empty( const Empty& ); 				// 拷贝构造函数
    	~Empty(); 							// 析构函数
    	Empty& operator=( const Empty& ); 	// 赋值运算符
    	Empty* operator&(); 				// 取址运算符
    	const Empty* operator&() const; 	// 取址运算符 const
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    1.1 缺省构造函数

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

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

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

    1.2 缺省拷贝构造函数

    • 函数名和类型名一样,有两种原型:
      • 参数为地址参数,为了防止无限构造,行成死循环;
      • const目的是常引用,不能改变里面的值;
    Empty(Empty &e);
    Empty(const Empty &e); // 这种规定在创建新对象的时候不得修改被拷贝的对象
    
    • 1
    • 2
    • 拷贝构造函数的参数必须是对象的引用,否则编译不过,然而这是为什么呢?
    // 如果不是引用,而是通过传值的方式将实参传递给形参,这中间本身就要经历一次对象的拷贝过程,而对象拷贝则必须调用拷贝构造函数,如此一来则会形成一个死循环,无解。所以拷贝构造函数的参数必须是对象的引用。
    
    • 1
    • 拷贝构造函数除了能用对象引用这样的参数,还能有其他参数,但这个参数必须给出默认值,如下:
    Empty(const Empty &e, int a = 5);
    
    • 1
    • 如果我们不声明一个拷贝构造函数,则会自动为类生成一个拷贝构造函数,它的功能简单,只能将原对象的所有成员变量复制给当前创建的对象,那么我们什么时候才能用到这个拷贝构造函数呢?
    #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<
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 拷贝构造函数参数为引用,系统自动生成的拷贝构造函数功能简单,只是将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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

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

    • 如果不想让对象发生拷贝行为,可以声明一个拷贝构造函数,并将其设置为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
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • 数据成员包含指针对象时,需考虑两种不同的处理需求:一种是复制指针对象,另一种是引用指针对象。拷贝构造函数大多数情况下是复制,而赋值函数是引用对象

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

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

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

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

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

    1.5 缺省取址运算符

    1.6 缺省const取址运算符

  • 相关阅读:
    c# string字符串连接原理
    leetcode 746 使用最小花费爬楼梯
    鲸鱼算法优化LSTM超参数-神经元个数-dropout-batch_size
    打破次元壁,让游戏角色在指尖跳舞,简易的 AR 教程
    【故障公告】周五下午的一次突发故障
    UE4 C++ 类的4种引用类型,和异步加载资产
    Python标准库之collections
    获取数据类型的方式和typescript is 类型谓词
    tcp连接+套接字编程
    如何提高网站安全防护?
  • 原文地址:https://blog.csdn.net/weixin_45805339/article/details/128089198