C++:类与对象(二)

一个类在我们不写任何内容时,系统也会自动生成一些成员函数,它们被称为"默认成员函数"。

本文我们将介绍其中的析构函数与构造函数。

一.构造函数

构造函数类似于我们在实现Stack时的Init,具有初始化实例对象的功能,其特点有以下这些:

1.名字与类相同

2.无返回值

3.对象实例化时系统自动调用

4.能够重载

5.如果类中没有显式定义构造函数,那C++编译器会自己生成一个无参的默认构造函数(注:不传实参都可以叫默认构造,而非特指系统生成的)

6.编译器生成的构造函数对于内置类型成员变量的初始化没有要求,而对于自定义类型,则会调用这个成员变量的默认构造函数,如果它没有,那就会报错。

cpp 复制代码
//自定义类型1:默认构造
class HasDefault
{
public:
	HasDefault()
	{
		cout << "HasDefualt的默认构造被调用" << endl;
	}
};


//自定义类型2:带参构造
class NoDefault
{
public:
	NoDefault(int x)
	{
		cout << "NoDefault的带参构造被调用, x = " << x << endl;

	}
};


//自定义类型3:编译器生成默认构造函数
class Outer
{
public:

private:
	int _a;
	double _b;
	HasDefault _hd;
	//NoDefault _nd; //此自定义成员变量无默认构造,加上此行会报错
};

int main()
{
	Outer o;
	cout << "Outer o 创建成功" << endl;
	
	return 0;
}

二.析构函数

析构函数的功能类似于Stack中的Destroy,通常负责对象中的资源释放工作。但需要注意,析构函数并非是销毁对象本身。

析构函数特点如下:

1.函数命是在类名前加上~

2.一个类中只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数

3.对象生命周期结束时,系统会自动调用

4.无参无返回值(无需加上void)

5.与构造函数类似的,不写析构函数时,编译器自动生成的对内置类型成员不做处理,但对于自定义成员,不论我们写不写,自定义类型成员会调用它的析构函数

6.一个局部域的多个对象,C++规定:后定义的先析构

第六点我们详细说说。

"后定义的先析构"原因在于局部对象存在栈上,比如下面这个例子:

cpp 复制代码
void func() {
    A a;      // ① 先定义
    B b;      // ② 后定义
    C c;      // ③ 最后定义
}             // 出作用域:c 先析构 → b → a(后定义的先析构)

如果包含全局对象和static修饰的对象呢?

cpp 复制代码
Global g;

void func()
{
    static StaticLocal sl;
    Local l1;
    Local l2;
}

int main()
{
    func();
    return 0;
}

由于刚刚那句话只在同类对象之间成立**,而static的生命周期是程序级,应该归到程序级销毁**,全局对象最晚。

因此,顺序应该是l2 - l1 - sl - g

如果func被调用两次呢?

static局部是"第一次执行到才会构造,之后不再构造,程序结束时才析构"

三.拷贝构造函数

浅拷贝

拷贝构造函数是一种特殊的构造函数,其用途是对自定义类型对象进行各种拷贝操作:

cpp 复制代码
Point a(1,2); //调用普通构造函数

Point b = a;  //调用拷贝构造,将a值拷贝给b

passByValue(b);; //传值传参触发拷贝构造,将实参数值拷贝给形参

Point c = passByValue(); //传值返回触发拷贝构造,把返回值拷贝给c

如果这里自己写拷贝构造,那就是这样:

cpp 复制代码
class Point
{
	int _x, _y;
public:
    //普通构造函数
	Point(int x = 0, int y = 0)
	{
		_x = x;
		_y = y;
		cout << "构造 Point( " << _x << ", " << _y << " )" << endl;
	}
    //拷贝构造函数
	Point(const Point& other) 
	{
		_x = other._x;
		_y = other._y;
		cout << "拷贝构造:复制了(" << other._x << "," << other._y <<  ")" << endl;
		
	}
	void show () const
	{
		cout << "(" << _x << ',' << _y << ")" << endl;
	}
};

你会发现,拷贝构造的参数和普通构造不同------拷贝构造函数的第一个参数 必须使用类类型对象的使用,使用传值方式,编译器会直接报错。

原因在于,传值这个行为也需要调用拷贝构造,结果调用的拷贝构造函数参数仍然是传值,结果又调用,以此类推,无限递归调用。

拷贝构造函数可以有多个参数,但是第一个参数外,后面的必须有缺省值。

深拷贝

上面所述称之为"浅拷贝",基本和赋值差不多,一般用编译器自己生成的函数没啥问题,但深拷贝是一个大坑。在动态申请内存空间的情景中,一定要自己去写拷贝构造。

比如这个场景:

cpp 复制代码
int main()
{
    Stack st1;
    st1.Push(1);
    st1.Push(2);
    
    Stack st2 = st1;
    return 0
};

st1有自己的空间,如果使用浅拷贝,那st1空间的地址就会被简单赋值给st2,导致二者共用同一块空间,导致增删数据混乱,析构时也会析构两次,程序崩溃。

这个问题也很好解决,只需要手写拷贝时,申请一份新空间给st2,把数据复制到这个空间就行了。

cpp 复制代码
//拷贝构造
Stack(const Stack& st)
{
	_a = (STDataType*)malloc(sizeof(int) * st._capacity);
	if (_a == nullptr)
	{
		perror("malloc");
		return;
	}
	memcpy(_a, st._a, sizeof(STDataType) * st._top);
	_top = st._top;
	_capacity = st._capacity;
}

赋值与拷贝构造的区别

请看示例

cpp 复制代码
#include <iostream>
using namespace std;

class Demo {
    int _id;
public:
    Demo(int id = 0) : _id(id) {
        cout << "构造 " << _id << endl;
    }
    Demo(const Demo& other) : _id(other._id) {
        cout << "拷贝构造 " << _id << endl;
    }
    Demo& operator=(const Demo& other) {
        _id = other._id;
        cout << "赋值 operator= " << _id << endl;
        return *this;
    }
};

int main() {
    Demo a(1);
    Demo b(2);

    Demo c = a;       
    c = b;             

    Demo d(a);        
    return 0;
}

在主函数中,Demo c = a是拷贝构造,因为c是"新对象",没内容,用a初始化,而后续的c = b就是赋值,因为c已经存在了,所以把b的值拷贝给c。(注:内置的"="是没办法这样用的,这里是使用了符号重载,专门"增加了"这个功能)

相关推荐
Dreams°1231 小时前
【Java后端+Vue前后端分离:内网正常、公网访问异常|5个高频经典踩坑完整复盘】
java·开发语言·vue.js
蒸蒸yyyyzwd1 小时前
cpp 选手备战秋招学习笔记 day23
c++·求职招聘
.YM.Z1 小时前
C++ STL 容器深度剖析:vector 与 list 的模拟实现及对比
开发语言·c++·vector·list
lsylalalala2 小时前
多线程(4)
java·开发语言·多线程
小白不想画工图2 小时前
银河麒麟V10系统安装QT5.12
linux·开发语言·qt·银河麒麟
IT_Octopus3 小时前
JSON 日志里的 `{“$ref“:“$.xxx“}`:从 fastjson 兼容包到原生 fastjson2 的迁移实录
开发语言·python·json
geovindu3 小时前
python:HandWriting Recognition using paddlepaddle
开发语言·后端·python·paddlepaddle
mengzhi啊3 小时前
QEventLoop loop配合loop.exec();替代while(1)。电脑cpu占用为0,使用while(1)cpu占用率100%
c++·qt
HugoStudio_SWAN3 小时前
洛谷 P1319 / P1320 压缩技术——同一枚硬币的正反面
c++·学习·程序人生·算法