构造函数、 拷贝构造函数 、 析构函数 和赋值运算符重载

本文详细介绍了C++中的构造函数、拷贝构造函数、析构函数及操作符重载的基本概念、特点和应用场景。重点讲解了构造函数的初始化列表、默认构造函数的生成条件以及拷贝构造函数的使用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        C++中,类的默认构造函数有六个,他们分别是构造函数,拷贝构造函数,析构函数,赋值操作符重载,取地址操作符重载和const修饰的取地址操作符重载。今天对其中的构造函数,拷贝构造函数,析构函数,赋值操作符重载几个默认函数进行简单的分析。

一、构造函数

   定义:构造函数是一个特殊的成员函数,名字与类名相同,创建类型变量时由编译器自动调用,在对象的生命周期内只且只调用一次,以保证每个数据成员都有一个合适的初始值。

   下面我们来看一个简单的例子:

  

   在此类中,有两个构造函数,编译器会根据参数的不同来决定调用哪个函数。

1、构造函数的特性:

   1)函数名与类名相同。

   2)没有返回值。

   3)有初始化列表(可以不用)

   4)新对象被创建,由编译器自动调用,且在对象的生命期

        内仅调用一次。

   5)构造函数可以重载,实参决定了调用那个构造函数。

   6)如果没有显式定义时,编译器会提供一个默认的构造函

        数。

   7)无参构造函数和带有缺省值得构造函数都认为是缺省构

        造函数,并且缺省构造函数只能有一个。

   8)构造函数不能用const来修饰。

下面对构造函数以上的个别几个特性进行解释:

第三条中说有初始化列表,下面对初始化列表进行详细介绍:

   定义:初始化列表是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据,成员后面跟一个放在园括号中的初始化式。

例如:


初始化顺序:

1、每个成员在初始化列表中只能出现一次。

2、初始化列表仅用于初始化数据成员,并不指定这些数据成员的初始化顺序,数据成员在类中定义顺序就是在参数列表中的初始化顺序。

3、尽量避免使用成员初始化成员,成员的初始化顺序最好和成员的定义顺序保持一致。(否则容易出现使用未初始化的成员去初始化另一个成员)

类中包含以下成员必须要放在初始化列表中初始化:

1)引用数据成员

2const数据成员

3)类类型成员(该类没有缺省的构造函数)

第六条中如果没有显示定义构造函数,系统会自动生成默认的构造函数:

2.默认构造函数

    类如果没有显式定义构造函数时,编译器会合成一个默认的构造函数,该构造函数中什么工作都不做。只要显式定义了,即使该构造函数什么也不做,编译器也不会为该类合成默认的构造函数。编译器生成的默认构造函数使用与变量初始化相同的规则来初始化成员,具有类类型的成员通过运行各自的默认构造函数来进行初始化。内置和复合类型的成员如指针、数组,只对定义在全局作用域中的对象初始化,当对象定义在局部作用域时,内置和符合类型的成员不进行初始

化。在某些情况下,默认构造函数是由编译器隐式使用的。

那么问题来了,编译器是否任何情况下都会自动生成默认的构造函数呢?下面我们就来验证一下:

 


 

上面两幅是一个Date类的源代码和反汇编代码,从源代码中红色圈标记处可以看出调试时不能在那个地方打断点,说明编译器根本没自动生成构造函数,从反汇编代码中也可以看到在创建Date类后函数并没有调用其他函数。

那这个不就和上面说的如果我们没有显示定义构造函数,编译器会自动为我们生成一个默认的构造函数相矛盾了吗?别着急,我们再来看下面一组例子:

class Time

{

public:

Time()

{

cout << "Time()" << endl;

}//无参构造函数

Time(const Time& t)

{

cout << "~Time(const Time& t)" << endl;

}//          构造函数的重载

private:

int _hour;

int _minute;

int _second;

};

class Date

{

private:

int _year;

int _month;

int _day;

Time t;

};

void FunTest()

{

Date d1;

}

int main()

{

FunTest();

system("pause");

return 0;

}

 

    从这个例子可以看出,编译器又给Date类自动生成了构造函数,那么编译器到底在什么情况下自动生成而在什么情况下不生成呢?下面我们来简单总结一下:

   不生成的情况:当编译器觉得构造函数什么也不用做时。

   自动生成的情况:

   假设有AB两个类,

     A 有自己缺省的构造函数                   

    B            没有显示的定义构造函数                           

   B中的类成员有A类型的变量,此时,系统会为B生成缺省的构造函数。

3.默认构造函数的作用:构建对象,初始化对象,类型转换。

二、 拷贝构造函数

  1.定义:只有单个形参,而且该形参是对本类类型对象的引用(常用

const修饰),这样的构造函数称为拷贝构造函数。拷贝构造函数是特殊的构造函数,创建对象时使用已存在的同类对象来进行初始化,由编译器自动调用。

class CDate

{

  public:

    CDate()

      {}

    CDate( const int year, const int month, const int day)

     {

          _iYear = year;

          _iMonth = month;

          _iDay = day;

     }

    CDate( const CDate& date)       //拷贝构造函数

     {

           _iYear = date._iYear;

           _iMonth = date._iMonth;

           _iDay = date._iDay;

     }

   private:

       int _iYear;

       int _iMonth;

       int _iDay;

};

2.特征

1)它是构造函数的重载。

2)它的参数必须使用同类型对象的引用传递。(不用创建临时变量)

3)如果没有显式定义,系统会自动合成一个默认的拷贝构造函数。默认的拷贝构造函数会依次拷贝类的数据成员完成初始化。(何时自动生成和上面的构造函数原理相同)

3,使用场景

1)对象实例化对象

CDate d1(1990, 1, 1);

CDate d2(d1);

2)传值方式作为函数的参数

void FunTest(const CDate date)

{}

3)传值方式作为函数返回值

CDate FunTest()

{

CDate date;

return date;

}

三、析构函数

1.定义:

析构函数:与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类的一些资源清理和汕尾工作。

2.特性

a、析构函数在类名(即构造函数名)加上字符~

b、析构函数无参数无返回值。

c、一个类有且只有一个析构函数。若未显示定义,系统会

自动生成缺省的析构函数。

d、对象生命周期结束时,C++编译系统系统自动调用析构函

数。

e、注意析构函数体内并不是删除对象,而是做一些清理工

作。

四、操作符重载

重载操作符是具有特殊函数名的函数,关键字operator后面接需要定义的操作符符号。操作符重载也是一个函数,具有返回值和形参表。它的形参数目与操作符的操作数目相同,函数调用操作符可以接受任意数目的操作数。使用运算符重载可以提高代码的可读性。返回类型 operate 操作符(参数列表)

下面我们以赋值操作符的重载来看:

#include<iostream>

using namespace std;

class Complex

{

public:

Complex(double r = 0.0, double i = 0.0)

:_real(r)

, _image(i)

{}

Complex& operator=(const Complex& d)

{

return Complex(_real = d._real, _image = d._image);

}

Complex& operator+(const Complex& c)

{

return Complex(_real + c._real, _image + c._image);

}

private:

double _real;

double _image;

};

void FunTest()

{

int a = 10;

int b = 20;

int c = 30;

a = b = c;

Complex c1(1.0, 2.0);

Complex c2(3.0, 4.0);

Complex c3;

c3 = c1 + c2;            重载

}

int main()

{

FunTest();

system("pause");

return 0;

}

结果:

 

在此程序案例中对赋值运算符进行了重载,实现了对两个类类型的变量的赋值。

 



### 构造函数析构函数、拷贝构造、赋值重载、取地址重载 #### 1. **构造函数** 构造函数是在创建类的对象时自动调用的特殊成员函数,用于初始化对象的状态。它与类名同名,并且没有返回类型。 **特点:** - 初始化对象的数据成员。 - 可以有多种形式(如默认构造函数、带参构造函数等)。 - 如果用户不提供任何构造函数,默认会生成一个无参的构造函数。 ```cpp class MyClass { public: int value; // 默认构造函数 MyClass() : value(0) {} // 带参构造函数 MyClass(int v) : value(v) {} }; ``` #### 2. **析构函数** 析构函数是在对象生命周期结束时自动调用的特殊成员函数,主要用于清理资源(如释放动态分配的内存)。它的名称也是类名前加波浪线`~`,并且没有任何参数返回类型。 **特点:** - 没有参数,也没有返回值。 - 主要用于释放资源或执行必要的清理工作。 - 每个类只能有一个析构函数。 ```cpp class MyClass { public: ~MyClass() { // 清理资源的操作 } }; ``` #### 3. **拷贝构造函数** 拷贝构造函数是一种特殊的构造函数,在通过已有的对象创建新对象时调用。它通常用于复制现有对象的所有数据到新的对象中。 **特点:** - 接受一个常量引用类型的同类对象作为参数。 - 当我们使用一个已有对象去初始化另一个新对象时会被调用。 ```cpp class MyClass { public: int* data; // 拷贝构造函数 MyClass(const MyClass& other) { if (other.data != nullptr) { data = new int(*other.data); } else { data = nullptr; } } // 资源管理相关的操作... }; ``` #### 4. **赋值重载** 赋值运算符重载是指对“=`”这个符号的行为进行自定义,使其可以处理复杂类型的赋值操作。对于非内置类型来说非常重要,特别是当需要深拷贝而不是浅拷贝的情况下。 **特点:** - 返回当前对象的引用(即 `*this`),以便支持链式赋值。 - 参数是一个常量引用类型的同类对象。 ```cpp class MyClass { public: int* data; // 赋值重载 MyClass& operator=(const MyClass& rhs) { if (this == &rhs) return *this; // 自我赋值检查 delete data; // 防止内存泄漏 if (rhs.data) { data = new int(*rhs.data); } else { data = nullptr; } return *this; } // 其他成员函数... }; ``` #### 5. **取地址重载 (`operator&`)** 虽然 C++ 支持对某些特定情况下的 `operator&` 进行重载,但在大多数情况下并不推荐这样做,因为这可能会导致代码难以理解以及潜在的问题。标准库中的智能指针实现了类似的功能而不需要直接修改语言级别的特性。 如果你确实想改变某个类如何响应取地址操作的话,则需谨慎考虑其合理性必要性。 --- 总结起来: - **构造函数** **析构函数** 分别负责对象的创建销毁过程; - **拷贝构造函数** 简化了对象间的复制操作; - **赋值重载** 则保证了安全有效的赋值机制; - 对于 **取地址重载**, 应尽量避免不必要的改动以保持程序的一致性易读性。 希望以上解释能帮助您更好地理解应用这些重要的 C++ 特性!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值