浅拷贝与深拷贝以及对象初始化列表和匿名对象问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/FAKER_0X0/article/details/89284570

类和对象部分

浅拷贝与深拷贝的问题
(Code::Blocks编译)

#include <iostream>
#include <cstring>
using namespace std;

//定义Teacher类
class Teacher
{
public:
    Teacher(int _id,const char *_name)  //  这里加const则不会出现warning
    {
        cout << "调用构造函数" << endl;
        id = _id;
        //复制字符串的内容,最后有空字符‘/0’
        int len = strlen(_name);
        name = new char(len+1);     //动态分配内存
        strcpy(name,_name);
    }
    ~Teacher()
    {
        if (name != NULL)
            delete name;    //释放占用的内存
        name = NULL;
        cout << "调用析构函数释放内存" << endl;
    }
    void printT()
    {
        cout << "id: " << id <<endl;
        //cout << "name: " << *name << endl; //输出第一个字符的值
        cout << "name: " << name << endl;   //输出字符串的值
    }
private:
    int id;
    char *name;
};

int main()
{
    Teacher t1(25,"lvxiaoning");
    t1.printT();
    Teacher t2(t1);
    t2.printT();
    return 0;
}

以上程序
t1用来初始化t2后会调用默认的拷贝构造函数,使得对象t1和t2的成员变量指向堆中的同一块内存,同一块内存被析构两次后程序不应该coredump吗?为何可以正确编译运行?

#define  _CRT_SECURE_NO_WARNINGS 
#include <iostream>

using namespace std;

class Teacher
{
public:
	//有参数的构造函数
	Teacher(int id, char *name)
	{
		cout << "调用了Teacher 的构造函数" << endl;
		//是给id 赋值
		m_id = id;

		//给姓名赋值
		int len = strlen(name);
		m_name = (char*)malloc(len + 1);
		strcpy(m_name, name);
	}

	//显示写一个拷贝构造函数
	//通过显示拷贝构造函数提供了深拷贝的动作
	//Teacher(const Teacher &another)
	//{
	//	m_id = another.m_id; //给id赋值

	//	int len = strlen(another.m_name);
	//	m_name = (char*)malloc(len + 1);

	//	strcpy(m_name, another.m_name);
	//}
	~Teacher() {
		//在构造函数中, 已经开辟了内存 所以为了防止泄露
		//在析构函数中,在对象销毁之前,把m_name的内存释放掉
		if (m_name != NULL) {
			free(m_name);
			m_name = NULL;
			cout << "释放掉了m_name" << endl;
		}
	}
private:
	int m_id;
	char *m_name;
};

int main(void)
{
	Teacher t1(1, "zhang3");

	//如果不提供一个显示的拷贝构造函数, 通过系统自带的默认拷贝构造函数
	Teacher t2(t1); //会调用t2的拷贝构造函数,将t1的值拷贝给t2

	return 0;
}

附例(VS2010编译)
以上例子在VS2015中时不需要写以下的头文件也可以正常编译运行,而Code::Blocks中则需要包含。

#include <cstring>		//
#include <stdlib.h>		//包含malloc(),free()函数

以上例子在VS2015中运行确实会出现以下图片显示的中断。。原因确如上述吧。QAQ
VS2015触发中断
结论:
例程序在VS2015中运行会出现由于调用默认拷贝函数(浅拷贝),同一块内存被析构两次后会触发中断异常;而在Code::Blocks中却能正常运行;说明不同的编译器对C++程序的响应不同,有时会得出截然不同的结果。


cstring和string头文件的区别

#include <cstring>   //不可以定义string s;可以用到strcpy等函数
#include <string> //可以定义string s;但是不可以用到strcpy等函数,属于STL范畴

更新
在VS2015中对应上例第一个的正确程序,重写拷贝构造函数,new要分配的是字符数组,采用中括号,小括号是初始化。

#define  _CRT_SECURE_NO_WARNINGS 
#include <iostream>

using namespace std;

//定义Teacher类
class Teacher
{
public:
	Teacher(int _id, const char *_name)  //  这里加const则不会出现warning
	{
		cout << "调用构造函数" << endl;
		id = _id;
		//复制字符串的内容,最后有空字符‘/0’
		int len = strlen(_name);
		name = new char[len + 1];     //动态分配内存
		strcpy(name, _name);
	}
	//使用默认的拷贝构造函数(浅拷贝)会触发断点
	//要使用深拷贝,重写拷贝构造函数
	Teacher(const Teacher &obj)
	{
		cout << "调用拷贝构造函数" << endl;
		id = obj.id;
		int len = strlen(obj.name);
		name = new char[len + 1];
		strcpy(name, obj.name);
	}
	~Teacher()
	{
		if (name != NULL)
		{
			delete [] name;    //释放占用的内存
			name = NULL;
			cout << "调用析构函数释放内存" << endl;
		}
	}
	void printT()
	{
		cout << "id: " << id << endl;
		//cout << "name: " << *name << endl; //输出第一个字符的值
		cout << "name: " << name << endl;   //输出字符串的值
	}
private:
	int id;
	char *name;
};

int main()
{
	Teacher t1(25, "lvxiaoning");
	t1.printT();
	Teacher t2(t1);
	t2.printT();
	return 0;
}

对象初始化列表和匿名对象的接收问题

//构造函数对象初始化列表练习
#include <iostream>
using namespace std;

class ABCD
{
public:
	ABCD(int _a, int _b, int _c)
	{
		a = _a;
		b = _b;
		c = _c;
		cout << "构造函数运行;a,b,c的值分别为:" << a << " " << b << " " << c << endl;
	}
	~ABCD()
	{
		cout << "析构函数运行;a,b,c的值分别为:" << a << " " << b << " " << c << endl;
	}
	int getA()
	{
		return a;
	}
private:
	int a;
	int b;
	int c;
};

class Mye
{
public:
	Mye() :abcd1(1, 2, 3), abcd2(4, 5, 6), m(100)	//对象初始化列表,类中包含的类和常整数声明
	{
		cout << "Mye()" << endl;
	}
	Mye(const Mye &obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
	{
		cout << "Mye(const Mye &obj)" << endl;
	}
	~Mye()
	{
		cout << "~Mye()" << endl;
	}
//private:
public:
	ABCD abcd1;
	ABCD abcd2;
	const int m;
};

int doThing(Mye mye1)
{
	cout << "mye1.abcd1.a = " << mye1.abcd1.getA() << endl;		//=7
	return 0;
}

//对象初始化列表的应用;类里包含的声明类的对象的构造函数先执行
int run()
{
	Mye mye;
	doThing(mye);	//调用拷贝构造函数
	return 0;
}

//匿名对象的接收问题
int run2()
{
	cout << "run2 start.." << endl;
	ABCD(400, 500, 600);	//产生的匿名对象会立即被析构
	ABCD abcd = ABCD(700, 800, 900);	
	cout << "run2 end.." << endl;
	return 0;
}

int main()
{
	//run();
	run2();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/FAKER_0X0/article/details/89284570