首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++: 06---构造函数析构函数

C++: 06---构造函数析构函数

作者头像
用户3479834
发布2021-02-03 15:33:30
发布2021-02-03 15:33:30
73700
代码可运行
举报
文章被收录于专栏:游戏开发司机游戏开发司机
运行总次数:0
代码可运行

拷贝构造函数: 用一个已经存在的对象来生成一个相同类型的新对象。(浅拷贝) 默认的拷贝构造函数: 如果自定义了拷贝构造函数,编译器就不在生成默认的拷贝构造函数。 如果没有自定义拷贝构造函数,但在代码中用到了拷贝构造函数,编译器会生成默认的拷贝构造函数。 深拷贝&浅拷贝: 系统默认的拷贝构造函数是浅拷贝,类中含有指针类型的变量,须自定义拷贝构造函数用深拷贝来实现。 浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,所指向的空间内容并没有复制,而是由两个对象共用。深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

如图:

思考: 当对象中存在指针成员时,为什么需要自己实现拷贝构造函数?如果不,会出现怎样的问题?

看代码:

代码语言:javascript
代码运行次数:0
运行
复制
#include<iostream>
class CGoods
{
public:
	CGoods(const char* name, double price, int amount)//带有三个参数的构造函数
	{
		std::cout << this << " :CGoods::CGoods(char*,float,int)" << std::endl;
		mname = new char[strlen(name) + 1]();
		strcpy(mname, name);
		mprice = price;
		mamount = amount;
	}
	CGoods()//不带参数的构造函数
	{
		std::cout << this << " :CGoods::CGoods()" << std::endl;
		mname = new char[1]();
	}
~CGoods()
	{
		std::cout << this << " :CGoods::~CGoods()" << std::endl;
		delete[] mname;
	}
private:
	char*  mname;
	double mprice;
	int mamount;
};
int main()
{
	CGoods good1("car1", 10.1, 10);
	CGoods good2 = good1;
	return 0;
}

程序运行结果:调一次构造函数,调两次析构函数。

分析:两个对象的指针成员所指内存相同,这会导致什么问题呢? mname指针被分配一次内存,但是程序结束时该内存却被释放了两次,会造成内存泄漏问题,这是一个不容忽视的问题。我们不得不自己写一个拷贝构造函数。

代码语言:javascript
代码运行次数:0
运行
复制
CGoods(const CGoods &rhs)
{
	std::cout << this << " :CGoods::CGoods(const CGoods &rhs)" << std::endl;
	mname = new char[strlen(rhs.mname) + 1]();
	strcpy(mname, rhs.mname);
	mprice = rhs.mprice;
	mamount = rhs.mamount;
}

程序运行结果

注意:拷贝构造函数传参必须传引用。若正常传,则会不断地递归去生成新形参对象,最后导致栈溢出。也不能用*,若写成 CGoods(const CGoods* rhs),就会变成一个构造函数,CGoods*传的是已存在对象的地址。 赋值运算符的重载函数: 用一个已存在的对象赋值给相同类型的已存在对象。(浅拷贝) 默认赋值运算符的重载函数: 赋值运算符重载函数用于类对象的赋值操作,当我们未实现该函数时,编译器会自动为我们实现该函数。同拷贝构造函数一样,系统默认的赋值运算符重载函数是浅拷贝,类中含有指针类型的变量,须自定义赋值运算符重载函数用深拷贝来实现。

代码语言:javascript
代码运行次数:0
运行
复制
CGoods& operator=(const CGoods& rhs)
{
	std::cout << this << " CGoods::operator=(const CGoods&)" << std::endl;
	if (this != &rhs)//判断是否给自己赋值
	{
		delete[] mname;//防止内存泄漏
		mname = new char[strlen(rhs.mname) + 1]();
		strcpy(mname, rhs.mname);
		mprice = rhs.mprice;
		mamount = rhs.mamount;
	}
	return *this;
}
int main()
{
	CGoods good1("car1", 10.1, 10);
	CGoods good2;
	good2 = good1;
	return 0;
}

程序运行结果:

思考:

为什么要避免自赋值呢? 1)自己给自己赋值完全是毫无意义,为了效率。

2)如果类的数据成员中含有指针,自赋值有时会导致灾难性的后果。对于指针间的赋值,先要将p所指向的空间delete掉,然后再为p重新分配空间,将被拷贝指针所指的内容拷贝到p所指的空间。如果是自赋值,那么p和被拷贝指针是同一指针,在赋值操作前对p的delete操作,将导致p所指的数据同时被销毁。

拷贝构造函数与赋值函数的区别?

    在看到“=”操作符为对象赋值的时候,

     如果是在对象定义时(Test B = (Test)c),此时调用拷贝构造函数;

    如果不是在对象定义赋值时(B = c),此时调用赋值函数。

    注:构造函数、拷贝构造函数,带有构造两个字,顾名思义,就是在对象声明或定义时才会使用。

拷贝构造函数与赋值函数定义的区别?

    内存空间角度:

      1)拷贝构造函数的使用,是在建立对象时;当时对象没有占有内存,故不需要释放内存,不重新建立内存空间。

      2)赋值函数的使用,是在对象建立后;当时对象已经占有内存,故需要释放先前内存,然后重新获取内存空间。

下面我们来看一个题目:

代码语言:javascript
代码运行次数:0
运行
复制
class String
{
public:
    String(const char *str = NULL); // 普通构造函数    
    String(const String &other);    // 拷贝构造函数
    ~String(void);                    // 析构函数    
    String & operator = (const String &other);    // 赋值函数
private:
    char *m_data;                    // 用于保存字符串
};

各个解析分别完成下上面String类声明的函数:

1、构造函数

代码语言:javascript
代码运行次数:0
运行
复制
/* 
   1、构造函数在构造对象时使用;
   2、传入参数的判断;
   3、对象的初始化问题。
*/
String::String(const char *str)
{
    if ( NULL == str)
    {
        m_data = new char[1]
        *m_data = '\0';
    }
    else
    {
        int len = strlen(str);
        m_data = new char[len + 1];
        strcpy(m_data,str);
    }
}

2、拷贝构造函数

代码语言:javascript
代码运行次数:0
运行
复制
/*
   1、拷贝构造函数必须在构造对象时使用,即定义对象时;
   2、对象初始化问题。
*/
String::String(const String &other)
{
    int len = strlen(other.m_data);
    m_data = new char[len+1];
    strcpy(m_data,other.m_data);
}

3、赋值函数

代码语言:javascript
代码运行次数:0
运行
复制
/*
   1、赋值函数使用时,对象肯定已经建立;
   2、赋值前,判断是否是自我赋值;
   3、赋值前,内存空间的准备:
       由于赋值前,对象已占有一定大小内存,但是赋值对象所占内存大小与
       对象已占的内存大小不一定一致;
       先释放对象已占的内存,然后分配心内存。
   4、正常赋值
*/
String & String::operator = (const String &other)
{
    if (&other == this)
    {
        return *this;
    }
    
    delete [] m_data;
    int len = strlen(other.m_data);
    m_data = new char[len+1];
    strcpy(m_data,other.m_data);
    
    return *this;
}

4、析构函数

代码语言:javascript
代码运行次数:0
运行
复制
/*
   资源的释放
*/
String::~String(void)
{
    delete []m_data;
}

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 拷贝构造函数与赋值函数的区别?
  • 1、构造函数
  • 2、拷贝构造函数
  • 3、赋值函数
  • 4、析构函数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档