C++类中四大隐者

C++类中四大隐者

小编想说的不是一灯、独孤求败、扫地僧、风清扬,而是C++类中的构造函数、析构函数、拷贝构造函数、赋值构造函数,初学C++功法的小编可是被这四大隐者打的“头破血流”

构造函数

1)构造函数的三大基本形式:类名(形参列表) : 初始化列表
class A
{
public:
	int a;
	A(void)
	{
        cout << "我是无参构造函数" << endl;
	}
	
	A(int _a)
	{
        a = _a;
        cout << "我是有参构造函数" << endl;
	}
	
	A(int num):a(num)
	{
        cout << a << endl;
        cout << "我是带初始化列表的构造函数" << endl;
	}
};

构造函数具有多个版本的功劳在于C++的函数重载功法

2)构造函数的作用与特点

  在知道了构造函数的基本形式后,我们需要了解我们为什么需要构造函数,以及构造函数有什么特点:
  ① 构造函数无需返回值

  ② 构造函数主要负责成员变量的初始化、分配相关资源、设置对象的初始状态

  ③ 构造函数在每每创建类对象时会被自动调用一次,在对象的生命周期中只会被调用一次

  ④ 当该类涉及继承与包含成员类时,构造函数有特定的调用顺序:先按照继承链依次调用父类中的构造函数 → 根据类成员变量的顺序依次调用类成员变量的构造函数 → 调用自身的构造函数

3)缺省构造函数

  缺省构造函数(相当于无参构造)是编译器自动生成的一个什么都不做的构造函数,目的是为了避免系统错误

所谓编译器自动生成的函数并不是语法意义上的函数,而是功能意义上的函数,编译器往往直接生成具有特定功能的二进制指令,而不需要借助高级语言(C++代码)

  需要注意的是当我们在类中实现一个有参构造后,无参构造函数变不再自动生成,如果需要使用到无参构造函数,必须显示的写在类中

class A
{
public:
	int a;
	A(int _a)
	{
        a = _a;
        cout << "我是有参构造" << endl;
	}
};

int main()
{
    A a;
}

上述在class A中定义了一个有参构造A(int _a)后,在main中定义对象A a时未提供参数,由于此时已经不会自动生成无参构造,会产生错误

4)无参构造函数

  隐者喜欢自问自答
  问:无参构造就是没有参数的构造函数吗?
  答:并非如此
  问:无参构造还有此等玄机?
  答:默认形参罢了

class A
{
public:
	int a;
	A(int _a = 10)
	{
        a = _a;
        cout << "我也算得上是无参构造" << endl;
	}
};

int main()
{
    A a;
}

当我们的class A中存在class B的类对象成员时,根据构造函数的调用顺序我们必须得保证class B中具备无参构造函数,因为在class A中定义B b时是无法初始化的

5)有参构造函数

  单参构造:形如A(int a) {}A a = 10这种语法也可能会成功,这是因为单参构造函数私底下实现了类型转换的功能(导致不调用赋值操作符,而是优先调用单参构造),这样的写法会给程序带来一定的危险,往往会在单参构造函数前加上explicit禁止该情况

6)带初始化列表的构造函数

  ① 为类成员初始化

  ② 执行初始化列表时,类成员变量在之前仍未成功定义

  ③ 成员的初始化顺序与初始化列表中的顺序无关,而是与定义顺序相关

class A
{
public:
	A(int _a)
	{
        cout << "A的有参构造函数" << endl;
	}
};

class B
{
public:
	B(int _b)
	{
        cout << "B的有参构造函数" << endl;
	}
};

class C
{
public:
	C(int _c)
	{
        cout << "C的有参构造函数" << endl;
	}
};

class All
{
    A a;
    B b;
    C c;
	All(int _a, int _b, int _c):c(_c),a(_a),b(_b)
	{
	
	}
};

int main()
{
    All all;
}

此时构造函数的调用顺序为A → B → C

析构函数

1)析构函数的基本形式:~类名(void)

2)析构函数的作用与特点

  析构函数处于构造函数的对立面,二者之间存在着不同,但是它们犹如阴阳两极,缺一不可
  ① 析构函数无返回值、无参数 → 无参数的规定导致其不能重载

  ② 析构函数会在销毁对象时自动调用,在对象的生命周期中最多被调用一次

class A
{
public:
	int a;
	A(void)
	{
        cout << "有参构造函数" << endl;	
	}
	~A(void)
	{
        cout << "析构函数" << endl;
	}
};

int main()
{
	//会调用析构函数
    A a1;

	//不会调用析构函数
    A* a2 = new A;
}

析构函数不一定被调用的原因在于还未来得及执行析构函数,进程便结束了

  ③ 析构函数主要负责释放在构造函数中获得的资源。当该类涉及继承与包含成员类时,析构函数有特定的调用顺序:调用自身的析构函数 → 根据类成员变量的顺序逆序调用类成员变量的析构函数 → 按照继承链逆序调用父类中的析构函数

注意:析构函数的调用次序与构造函数的调用次序正好相反

3)缺省的析构函数

  ① 如果一个类中没有实现析构函数,编译器会自动生成一个具有析构函数功能的二进制指令,用来负责释放编译器所能看见的资源(成员变量、类成员、父类成员)

  ② 什么是编译器看不见的资源?天下武功唯快不破,看不见的资源便是动态资源(我们可以初步将其理解为堆内存),缺省的析构函数无法释放动态资源,当类中存在有关动态资源的分配时,我们必须定义一个专门的析构函数来进行资源管理与善后处理

赋值构造

1)赋值构造的基本形式
class Student
{
public:
	char* name;
	//构造函数
	Student(const char* name)
	{
        this->name = new char[strlen(name)+1];
        strcpy(this->name,name);
	}
	
	//析构函数	
	~Student(void)
	{
        delete[] name;
	}
	
	//赋值构造
	Student& operator=(const User& that)
	{
        if(this != &that)
        {
            //释放原来的空间
            delete[] name;
			
            //申请新空间
            name = new char[strlen(that.name)+1];
			
            //复制内容
            strcpy(name,that.name);
        }
	}
}

知道运算符重载的同学们不难发现,赋值构造其实便是对=操作符的重载,返回值设置成Student&的目的是为了能够连续赋值

2)赋值构造的作用与特点

  ① 当某一个类的旧对象给该类的另一个旧对象赋值时,调用赋值构造

  ② if(this != &that)是为了能够自己给自己赋值,若缺少该判断语句,则会导致逻辑错误,将自己释放后如何再给自己赋值呢?

  ③ 问题又来了,为什么要先释放内存呢?因为当我们调用赋值构造时,that对象中的成员可能在之前通过new申请的字节数少小于我们this对象所需申请的字节数,所以需要重新设置申请内存的大小

3)缺省的赋值构造

  ① 缺省时编译器会生成一个赋值构造函数,它负责把一个对象的内存拷贝给另一个对象

  ② 当需要深拷贝时,缺省的赋值构造无法满足需求,此时必须手动实现赋值构造

浅拷贝:复制了对象的引用地址,使得两个对象指向同一块内存地址,修改任意一边都会
    同时变化

深拷贝:复制了对象与其值,拥有自己的内存地址,修改任意一边不会影响彼此

拷贝构造

1)拷贝构造的基本形式
class Student
{
public:
	char* name;
	//构造函数
	Student(const char* name)
	{
        this->name = new char[strlen(name)+1];
        strcpy(this->name,name);
	}
	
	//析构函数	
	~Student(void)
	{
        delete[] name;
	}
	
	//拷贝构造
	Student(Student& that)
	{
        name = new char[strlen(that.name)+1];
        strcpy(name,that.name);
	}
2)拷贝构造的作用和特点

  ① 拷贝构造也称为复制构造

  ② 当某一类的旧对象给该类的新对象赋初值的时候调用拷贝构造

  ③ 当将对象作为函数参数时,调用函数将会一起调用拷贝构造(给形参赋初值)
  

3)缺省的拷贝构造

  ① 缺省时,编译器会自动生成一个拷贝构造函数,它负责把旧对象中的所有数据给新创建的对象

  ② 当需要深拷贝时,即类中有指针成员,缺省的拷贝构造函数便无法完成任务,此时必须手动实现拷贝构造

因此不难发现,当涉及到深拷贝时,拷贝构造与赋值构造同时需要我们手动实现

总结

  小编认为其实C++中最大的隐士还是C语言,小编整理的四个隐藏函数仍停留在基础上,涉及到各种不同的继承后它们还有一些独特之处。若有不正确的地方,希望大佬们及时指出。共勉!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值