构造、析构、拷贝

本文详细介绍了C++中的构造函数、析构函数及其应用场景。重点讨论了构造函数的注意事项,如自定义构造函数后默认构造函数和拷贝构造函数的变化。此外,还讲解了析构函数的执行顺序,并给出了析构函数与构造函数执行顺序相反的规律。文章还深入探讨了浅拷贝与深拷贝的概念,强调了深拷贝在处理指针成员时防止内存泄漏的重要性,通过实例代码进行了说明。

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

构造函数

  • 类提供默认的无参构造函数和拷贝构造函数,如果自己手动添加构造函数,默认将不复存在,如果提供显示的构造,默认无参构造将会被隐藏,但是默认拷贝还在;如果显示的写了一个拷贝构造函数 ,会隐藏默认的无参构造函数和默认的拷贝构造函数

构造注意小细节

class Person
{
public:
	Person()
	{
		cout << "无参构造" << endl;
	}
	Person(int a)
	{
		cout << "有参构造" << endl;
	}
};

//  调用情况1.
Person p;   //调用了无参构造
Person p();  //没有调用了无参构造, 


// 调用情况2.
Person* p1 = new Person; // //调用了无参构造
Person* p2 = new Person();  //调用了无参构造

tips : 省事的话调用无参构造,直接不加括号



析构函数

  • 类提供默认的析构函数,如果手动添加默认将不存在
  • 析构顺序和构造顺序相反

应用场景

Test.h

class Test
{
public:
	Test(int x, int y)
	{
		m_x = x;
		m_y = y;
		cout << "调用了有参数的构造函数" << endl;
	}

	//无参数的构造函数
	Test(){
		m_x = 0;
		m_y = 0;
		cout << "调用了无参数的构造函数" << endl;
	}


	//拷贝构造函数 ,想通过另一个Test对象 another 将本对象进行拷贝
	Test(const Test & another)
	{
		m_x = another.m_x;
		m_y = another.m_y;
		cout << "调用了拷贝构造函数" << endl;
	}

	//等号操作符
	void operator = (const Test &t)
	{
		cout << "调用了=号操作符" << endl;
		m_x = t.m_x;
		m_y = t.m_y;
	}

	void printT()
	{
		cout << "x : " << m_x << ", y : " << m_y << endl;
	}

	//提供一个析构函数
	~Test()
	{
		cout << "~Test()析构函数被执行了" << endl;
		cout << "(" << m_x << ", " << m_y << ")" << "被析构了" << endl;
	}

private:
	int m_x;
	int m_y;
};

1. 
void test1()
{
	Test t1(1, 2); //调用t1的有参构造
	Test t2(t1); //调用 t2的拷贝构造
	//通过t1 给t2 进行赋值
	t2.printT();
}



2.
void test2()
{
	Test t1(1, 2);
	Test t2;
	t2 = t1; //调用的不是拷贝构造函数,调用的是=号操作符,也能够完成将t1的值给t2 但不是调用t2的拷贝构造函数。
}


3.
void func(Test t) //Test t(t1); //会调用局部变量t的拷贝构造函数
{
	cout << "func begin..." << endl;
	t.printT();
	cout << "func end..." << endl;

}
void test3()
{
	cout << "test3 begin ..." << endl;
	Test t1(10, 20); //创建了一个t1的对象。通过t1的有参数的构造函数
	func(t1);

	cout << "test3 end..." << endl;
}





4. 
Test func2()
{
	cout << "func2 begin..." << endl;
	Test temp(10, 20); //调用temp的带参数构造函数  
	cout << "func2 end.." << endl;
	return temp; // 有一个临时的匿名对象 = temp ,把temp的数据给到了临时的匿名对象,  会调用这个临时匿名对象的拷贝构造函数, 将temp传进去。
				
}
void test4() 
{
	cout << "test4 begin " << endl;
	func2();

	//匿名对象在此被析构了, 如果一个临时的匿名对象,没有任何变量去接收它,编译器认为这个临时匿名对象没有用处。
	//编译器会立刻销毁这个临时的匿名对象
	cout << "test4 end" << endl;
}



5.
void test5()
{
	cout << "test5 begin ..." << endl;
	Test t1 = func2();//如果有一个变量去接收这个临时的匿名对象, 编译器认为这个匿名对象转正了,就不会立刻给他销毁。
							//t1 = 匿名的临时对象 为什么不会发生拷贝构造
							//	此时的t1 去接收这个匿名的临时对象不是 重新创建一个t1 而是给这个匿名对象起个名字就叫t1
							//一旦这个匿名对象有了自己的名字,编译器就不会立刻给这个匿名对象销毁了,
							//就当普通局部变量处理了

	cout << "test5 end..." << endl;

	//在此时析构的t1
}




6.
void test6()
{
	cout << "test6 begin..." << endl;
	Test t1; //调用t1的无参数构造函数
	t1 = func2(); //调用的=号操作符 ,,t1 = 匿名对象。 调用了t1的=号操作符。
						//此时匿名没有被转正,匿名没有自己的名字, 匿名对象这个内存没有自己的别名, 编译器就会立刻销毁。
	cout << "test6 end..." << endl;
}

浅拷贝、深拷贝

  • 有时候成员变量具有指针特性,一旦进行拷贝,指针指向同一块内存,当释放的时候,如果同一块内存被两次释放,程序将崩溃。这称之为浅拷贝。深拷贝的意思是将拷贝构造或者 =操作符重载的函数重写,重新开辟一块内存给需要赋值的指针,这样浅拷贝的问题将解决。

例子:

person.h

#pragma once
#include <iostream>
using namespace std;


class Person
{
public:
	//有参数的构造函数
	Person(int id, const char *name)
	{
		m_id = id;
		int len = strlen(name);
		m_name = (char*)malloc(len + 1);
		strcpy_s(m_name, len + 1, name);
	}

	Person(const Person& another)
	{
		m_id = another.m_id;
		int len = strlen(another.m_name);
		m_name = (char*)malloc(len + 1);
		strcpy_s(m_name, len+1, another.m_name);
	}

	~Person()
	{
		if (m_name != NULL)
		{
			free(m_name);
			m_name = NULL;
		}
	}

private:
	int m_id;
	char *m_name;
};

user.c

#include "person.h"
using namespace std;


int main()
{
	Person t1(1, "zhang3");

	//如果不提供一个显示的拷贝构造函数, 通过系统自带的默认拷贝构造函数,程序会崩溃
	Person t2(t1); //会调用t2的拷贝构造函数,将t1的值拷贝给t2
    return 0;
}
### C++ 中构造函数、函数和拷贝构造函数的详解 #### 1. 构造函数 构造函数是在对象被创建时自动调用的一种特殊成员函数,用于初始化对象的状态。如果没有显式定义任何构造函数,则编译器会自动生成一个默认构造函数[^1]。 - **默认构造函数** 默认构造函数是一个没有任何参数或者所有参数都有默认值的构造函数。它通常用来初始化对象到某种初始状态。需要注意的是,默认构造函数并不是完全“空”的,在某些情况下可能会执行一些必要的操作来确保对象处于有效状态[^3]。 - **全缺省构造函数** 当构造函数的所有参数都提供了默认值时,这种构造函数也可以作为默认构造函数使用。例如: ```cpp Example(int val = 0); ``` - **带参构造函数** 带参构造函数允许通过传递参数的方式初始化对象的数据成员。例如: ```cpp Example e2(10); // 调用带参数的构造函数 ``` #### 2. 函数 函数是一种特殊的成员函数,当对象生命周期结束时会被自动调用。它的主要作用是用来释放资源,比如关闭文件、释放动态分配的内存等。函数没有返回类型,也没有参数,并且其名称与类名相同,前面加波浪号 `~` 表示[^2]。 - 示例代码如下: ```cpp ~Example() { cout << "Destructor called" << endl; } ``` #### 3. 拷贝构造函数 拷贝构造函数是一种特殊的构造函数,用于从已存在的同类型对象初始化新对象。如果未显式定义拷贝构造函数,编译器会提供一个默认版本,该版本逐位复制数据成员。然而,对于涉及指针或其他复杂资源管理的情况,这可能导致问题,因此常常需要自己定义拷贝构造函数[^3]。 - 示例代码如下: ```cpp Example(const Example& other) : value(other.value) { cout << "Copy constructor called" << endl; } ``` #### 关键区别总结 | 特性 | 构造函数 | 函数 | 拷贝构造函数 | |---------------------|-----------------------------------|------------------------------|-------------------------------| | 自动调用时机 | 创建对象时 | 销毁对象时 | 使用已有对象初始化新对象时 | | 参数 | 可有可无 | 无 | 接收常量引用类型的现有对象 | | 主要用途 | 初始化对象 | 清理资源 | 复制对象 | ```cpp #include <iostream> using namespace std; class Example { public: // 默认构造函数 Example() { cout << "Default constructor called" << endl; } // 带参构造函数 Example(int val) : value(val) { cout << "Parameterized constructor called with value: " << val << endl; } // 拷贝构造函数 Example(const Example& other) : value(other.value) { cout << "Copy constructor called" << endl; } // 函数 ~Example() { cout << "Destructor called" << endl; } void print() const { cout << "Value: " << value << endl; } private: int value; }; int main() { Example obj1; // 调用默认构造函数 Example obj2(42); // 调用带参构造函数 Example obj3(obj2); // 调用拷贝构造函数 obj1.print(); obj2.print(); obj3.print(); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值