C++ Primer 中级(11-20)

析构函数


#include<iostream>
#include<string>
#include<vector>

using namespace std;

/*
如果类中有数据指针成员且构造函数中使用new初始化,那么必须自行写析构函数;
如果写了析构函数,那么一定要写复制构造函数和赋值函数。
*/

class NoName
{
public:
	NoName() :pstring(new std::string), i(0), d(0) {}

	~NoName();// declaration of destruction

	// 定义复制构造函数
	NoName(const NoName& other)
		:pstring(new std::string(*(other.pstring))),
		i(other.i),
		d(other.d)
	{}



	// 定义赋值函数
	NoName& operator=(const NoName& rhs)
	{
		pstring = new std::string;
		*pstring = *rhs.pstring;
		i = rhs.i;
		d = rhs.d;
	}

private:
	std::string* pstring; // pointer
	int i;
	double d;

};

NoName::~NoName()
{
	cout << "析构函数被调用" << endl;
	delete pstring;
}

int main()
{
	NoName a;

	NoName* p = new NoName;

	cout << "Hello\n" << endl;

	delete p; // 删除p指向的NoName对象的数据指针成员
	cout << "ok,delete p" << endl;

	return 0;//第二次调用析构函数删除对象a中的数据指针成员你

}

复制构造函数:浅复制

#include <iostream>
#include<string>

using namespace std;

class CDemo
{
public:
	CDemo(int pa, const char* cstr)
	{
		this->a = pa;
		this->str = new char[1024];
		strcpy_s(this->str,1024, cstr);
	}

	// C++自动生成的复制构造函数大概是这样的
	CDemo(CDemo & obj)
	{
		this->a = obj.a;
		this->str = obj.str; //浅复制
	}

public:
	int a;
	char* str;
};

/*
深复制,复制的是内容;
浅复制,复制的是内容所在存储空间的地址;

*/
int main()
{
	CDemo A(10, "hello");

	CDemo B=A;
	//如果自己没有写复制构造函数,那么这里会调用C++的复制构造函数(执行浅复制)

	B.a = 8;
	//TEST 这确实是一个浅复制
	B.str[0] = 'k'; 

	cout <<"A="<<A.a<<"," << A.str << endl; // 结果发现A中的str被修改
}
/*
对象是两个,但是对象所引用的资源是只有一个;

此时,如果其中一个对象被析构,那么所指向的资源也会被释放,如此一类,另外一个对象的指针会变成野指针
*/

复制构造函数:深复制


#include <iostream>
#include<string>

using namespace std;

class CDemo
{
public:
	CDemo(int pa, const char* cstr)
	{
		this->a = pa;
		this->str = new char[1024];
		strcpy_s(this->str,1024, cstr);
	}

	// C++自动生成的复制构造函数大概是这样的
	CDemo(CDemo & obj)
	{
		this->a = obj.a;
		this->str = new char[1024]; //深复制
		strcpy_s(this->str, 1024, obj.str);
	}

	~CDemo()
	{
		delete str;
	}
public:
	int a;
	char* str;
};

/*
深复制,复制的是内容;
浅复制,复制的是内容所在存储空间的地址;

*/
int main()
{
	CDemo A(10, "hello");

	CDemo B=A;
	//如果自己没有写复制构造函数,那么这里会调用C++的复制构造函数(执行浅复制)

	B.a = 8;
	//TEST 这确实是一个深复制
	B.str[0] = 'k'; 

	cout <<"A="<<A.a<<"," << A.str << endl; // 结果发现A中的str没有被修改
}

管理指针成员

智能指针

在这里插入图片描述

深复制会产生大量对象,有时候我们希望若干指针指向同一个空间,但是又不希望

/*plain_ptr.h头文件内容是这样的*/
//#pragma once
// 浅复制
class AHasptr {
public:
	AHasptr(int *p,int i):ptr(p),val(i){}

	int* get_ptr() const { return ptr; }
	int get_int() const { return val; }

	void set_ptr(int* p) { ptr = p; }
	void set_int(int i) { val = i; }

	int get_ptr_val() const { return *ptr; }
	void set_ptr_val(int val) const
	{ *ptr = val;}//why it can do. const,  ans : we cannot modify the address


private:
	int val;
	int* ptr;
};

下面测试在源文件中使用头文件定义的类,感受浅复制。。

#include <iostream>
#include "plain-ptr.h"

using namespace std;

void test_AHasPtr()
{
	int i = 42;
	AHasptr p1(&i, 42);
	AHasptr p2 = p1;
	cout << p2.get_ptr_val() << endl;

	// now, modify the int_value to which p1's poninter point
	p1.set_ptr_val(2);
	// then, find the value of p2 has changed
	cout << p2.get_ptr_val() << endl;

}

int main()
{
	test_AHasPtr();

	int* ip = new int(42);
	AHasptr ptr(ip, 10);
	cout << ptr.get_ptr_val() << endl;
	delete ip;
	//此时对象ptr中的指针成员会变成野指针,指向一个无效的地址
	cout << ptr.get_ptr_val() << endl;
	
	return 0;
}

深拷贝的例子

头文件的代码如下

//#pragma once
// 深拷贝的类-头文件
class CHasPtr
{
public:
	CHasPtr(int p, int i) // 使用引用,创建了一个新空间接受这个值
		:ptr(new int(p)), val(i) {}

	int* get_ptr() const { return ptr; }
	int get_int() const { return val; }

	void set_ptr(int* p) { ptr = p; }
	void set_int(int i) { val = i; }

	int get_ptr_val() const { return *ptr; }
	void set_ptr_val(int i) const { *ptr = i; }

	// 深复制需要自行编写复制构造函数
	CHasPtr(const CHasPtr& orig)
	{
		this->val = orig.val;
		this->ptr = new int(*(orig.ptr));
	}

	//深复制需要自行编写赋值重载函数
	CHasPtr& operator=(const CHasPtr& orig)
	{
		CHasPtr temp(*(orig.ptr), orig.val);
		return *this;
	}

	//如果自行编写复制构造函数,那么也应该编写析构函数
	~CHasPtr()
	{
		delete ptr;
	}



private:
	int val;
	int* ptr;
};

在源文件中,体会深拷贝……

#include <iostream>
#include "plain-ptr.h"
#include "value-ptr.h"

using namespace std;

void test_AHasPtr()
{
	int i = 42;
	AHasptr p1(&i, 42);
	AHasptr p2 = p1;
	cout << p2.get_ptr_val() << endl;

	// now, modify the int_value to which p1's poninter point
	p1.set_ptr_val(2);
	// then, find the value of p2 has changed
	cout << p2.get_ptr_val() << endl;

	int* ip = new int(42);
	AHasptr ptr(ip, 10);
	cout << ptr.get_ptr_val() << endl;
	delete ip;
	//此时对象ptr中的指针成员会变成野指针,指向一个无效的地址
	cout << ptr.get_ptr_val() << endl;

}

void test_CHasPtr()
{
	cout << "deep copy" << endl;

	int obj = 0;
	CHasPtr ptr1(obj, 10);

	CHasPtr ptr2(ptr1);
	cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
	cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;

	cout << " now, modify ptr1's-value" << endl;
	ptr1.set_ptr_val(20);
	cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
	cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;
}

int main()
{
	test_AHasPtr();
	cout << endl;
	test_CHasPtr();

	return 0;
}

下面是输出结果,可以看到修改ptr1,但是ptr2的值并没有发生改变
在这里插入图片描述
深复制,又被称作值型类,这是因为借鉴了整数型变量的复制,比如整数型变量的复制,一个变量的修改就不会影响到另外一个。

然而,深复制每个指针都有一个对象,有时候会占用大量内存,因此考虑使用智能指针

自己写智能指针类

new一个智能指针

智能指针里面有一个普通指针指向公用对象

计数,计算有多少对象指针使用这个共用对象

如果计数不为0,意味着有对象在引用公用对象是,那么就不能删除这个共用对象;

如果为0,可以删除

可以根据计数,对指针进行管理

在这里插入图片描述
可以看到智能指针对象里面的指针成员地址是一样的,如果修改一个,另一个也会发生变化。

#pragma once
//自行编写一个智能指针类
/*
ptr-ip,表示智能指针里面有一个普通指针

复制构造函数的调用,使得其多了一个对象使用、指向共用对象空间,因此也要++

++ptr->use
 反过来,析构要--,如果计数为0,那么就把它删除掉

改一个对象,就会修改到公用对象的内容

智能指针通过计数,避免了野指针,
注:在vs2019,会出现重复删除的问题……
*/
class U_Ptr
{
private:
	int* ip;
	size_t use; // to conut

	U_Ptr(int *p):ip(p),use(1){}
	~U_Ptr() { delete ip; }

	friend class BHasPtr;
};

/*暂时的理解:BHasPtr中的成员指针是智能指针(U_Ptr),智能指针里面是普通指针*/
class BHasPtr
{
public:
	BHasPtr(int* p,int i)//:ptr(new U_Ptr(p),val(i))
	{
		ptr = new U_Ptr(p);
		val = i;
	}

	BHasPtr(const BHasPtr &orig):ptr(orig.ptr),val(orig.val)
	{
		++ptr->use;
	}

	~BHasPtr()
	{
		if (--ptr->use == 0)
			delete ptr; // why not ptr->ip
	}
	int* get_ptr() const { return ptr->ip; }
	int get_int() const { return val; }

	void set_ptr(int* p) { ptr->ip = p; }
	void set_int(int i) { val = i; }

	int get_ptr_val() {return *(ptr->ip); } // ptr是一个指针,指向U_Ptr对象,可以把它U_Ptr类中的this
	void set_ptr_val(int val) { *(ptr->ip) = val; }

	BHasPtr& operator=(const BHasPtr& rhs)
	{
		++rhs.ptr->use;
		if (--ptr->use == 0) delete ptr;

		ptr = rhs.ptr;
		val = rhs.val;
		return *this;
	}

private:
	int val;
	U_Ptr* ptr;
};

测试智能指针的源文件

#include <iostream>
#include "plain-ptr.h"
#include "value-ptr.h"
# include "smart-ptr.h"

using namespace std;

void test_AHasPtr()
{
	int i = 42;
	AHasptr p1(&i, 42);
	AHasptr p2 = p1;
	cout << p2.get_ptr_val() << endl;

	// now, modify the int_value to which p1's poninter point
	p1.set_ptr_val(2);
	// then, find the value of p2 has changed
	cout << p2.get_ptr_val() << endl;

	int* ip = new int(42);
	AHasptr ptr(ip, 10);
	cout << ptr.get_ptr_val() << endl;
	delete ip;
	//此时对象ptr中的指针成员会变成野指针,指向一个无效的地址
	cout << ptr.get_ptr_val() << endl;

}

void test_CHasPtr()
{
	cout << "deep copy" << endl;

	int obj = 0;
	CHasPtr ptr1(obj, 10);

	CHasPtr ptr2(ptr1);
	cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
	cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;

	cout << " now, modify ptr1's-value" << endl;
	ptr1.set_ptr_val(20);
	cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
	cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;
}

void test_BHasPtr()
{
	int obj = 0;

	BHasPtr ptr1(&obj, 42);
	BHasPtr ptr2(ptr1);
	
	cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
	cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;

	cout << " now, modify ptr1's-value" << endl;
	ptr1.set_ptr_val(20);
	cout << "ptr1-ptr-address: " << ptr1.get_ptr() << ", ptr1's value:" << ptr1.get_ptr_val() << endl;
	cout << "ptr2-ptr-address: " << ptr2.get_ptr() << ",ptr2's value: " << ptr2.get_ptr_val() << endl;
}
int main()
{
	cout<<"常规指针,浅拷贝:" << endl;
	test_AHasPtr();
	cout << endl;
	test_CHasPtr();
	cout << endl;
	cout << "智能指针:" << endl;
	test_BHasPtr();


	return 0;
}

运算符重载

重载操作符不会改变优先级,

重载操作函数,可以是成员函数,也可以是友元函数;

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

class Sales_item
{
	friend ostream& operator<<(ostream& out, Sales_item& item);
	friend istream& operator>>(istream& in, Sales_item& item);

public:
	Sales_item(const string &book,unsigned count, double price)
		:isbn(book),units_sold(count),revenue(count*price){}
	Sales_item():units_sold(0),revenue(0){}

	Sales_item& operator+=(const Sales_item& item);
	

private:
	string isbn;
	unsigned units_sold;
	double revenue;
};

ostream& operator<<(ostream& out, Sales_item& item)
{
	out << item.isbn << "\t" << item.units_sold << "\t" << item.revenue << endl;
	return out;
}

// generally , operator+= is a member function, operator+ is a friend function;
Sales_item& Sales_item::operator+=(const Sales_item& item) // operator+= 更新当前对象,因此函数的返回值是引用
{
	this->units_sold += item.units_sold;
	this->revenue += item.revenue;
	return *this;
}

Sales_item operator+(const Sales_item& item1, const Sales_item& item2)// operator+ 函数会返回一个新对象,因此不能返回类的引用
{
	Sales_item itemsum(item1);
	itemsum += item2;
	return itemsum;
}


istream& operator>>(istream& in, Sales_item& item)
{
	double price;
	in >> item.isbn >> item.units_sold >> price;
	if (in)
		item.revenue = price * item.units_sold;
	else // error occurs, we need to 保证对象的数据处在一致的状态
		item = Sales_item();
	return in;
}


int main()
{
	Sales_item item1("001", 2, 40);
	cout << item1 << endl;
	cout << "operator>>:" << endl;
	cin >> item1;
	cout << item1;

	cout << endl;
	cout << "operator+_TEST: " << endl;
	Sales_item item2("002", 4, 80);
	Sales_item result;
	result = item1 + item2;
	cout << result << endl;
	cout << "operator+=_TEST: " << endl;
	item1 += item2;
	cout << item1 << endl;

	return 0;
}

关系操作符重载

沿用上面operator+=, operator+的思路:
等于不等于一起重载,如果等于已经重载,那么考虑重载不等于的函数中调用等于

#include <iostream>
#include<stdlib.h>
using namespace std;

class Date
{


public:
	Date(int m=0,int d=0, int y=0):month(m),year(y),day(d){}

	int operator==(const Date& dt) const;
	int operator<(const Date& dt) const;

private:
	int year, month, day;

};

int Date::operator==(const Date& dt) const
{
	return (year == dt.year) && (month == dt.month) && (day == dt.day);
}

int operator!=(const Date& dt1, const Date& dt2)
{
	return !(dt1 == dt2);
}

int Date::operator<(const Date& dt) const
{
	if (year == dt.year)
	{ if (month == dt.month)
			return day < dt.day;
	return month < dt.month;
	}
	else
		return year < dt.year;
}


int main()
{
	Date d1(2, 14, 2020);
	Date d2(3, 4, 2020);
	Date d3(2, 16, 2020);

	if (d1 < d2)
		cout << "d1 < d2 " << endl;
	if  (d1 != d3)
		cout << "d1!=d3" << endl;

	system("pause");
	return 0;
}
#include <iostream>
#include<stdlib.h>
using namespace std;

class String
{
public:
	String(const char* p)
	{
	p = p ? p : ""; // if p is a null-pointer, then we 把它化为空字符串;
	ptrChar = new char[std::strlen(p) + 1];
	strcpy_s(ptrChar, 10,p);
	}

	String& operator=(String const&); // 赋值操作符的重载

	void print()
	{
		cout << ptrChar << endl;
	}
private:
	char* ptrChar;
};

String& String::operator=(String const& str)
{
	if (std::strlen(str.ptrChar) != std::strlen(ptrChar))
	{
		char* don = new char[std::strlen(str.ptrChar) + 1];//delete[] ptrChar;
		delete[] ptrChar;
		ptrChar=don; //char* ptrChar = new char[std::strlen(str.ptrChar) + 1];
	}
	strcpy_s(ptrChar, 10,str.ptrChar);
	return *this;
}

int main()
{
	String s("dog");
	String h("hello");

	 s.print() ;

	 // use operator= function
	 s = h;
	 s.print();


	system("pause");
	return 0;
}

下标操作符的重载

#include <iostream>
#include<stdlib.h>

using namespace std;

class String
{
public:
	String(const char* p)
	{
	p = p ? p : ""; // if p is a null-pointer, then we 把它化为空字符串;
	ptrChar = new char[std::strlen(p) + 1];
	strcpy_s(ptrChar, 10,p);
	}

	char& operator[](std::size_t index);//  throw(String); // 下标操作符的重载
	char& operator[](std::size_t index) const;

	void print()
	{
		cout << ptrChar << endl;
	}
private:
	char* ptrChar;
	static String errorMessage; // 显示报错信息
};

String String::errorMessage("Subscript out of range!"); // initialize this static memeber


char& String::operator[](std::size_t index) // throw(String)
{
	//if (index >= std::strlen(ptrChar))
		//throw errorMessage;
	return ptrChar[index];
}

// 下标操作符重载:需要两个成员函数,1可变成员函数,2常量成员函数
// reason is that 对常变量s2来说,只能调用const成员函数;

char& String::operator[](std::size_t index) const// throw(String)
{
	//if (index>=std::strlen(ptrChar))
		//throw errorMessage;
	return ptrChar[index];
}


int main()
{
	String s("dog");
	String h("hello");

	 s.print() ;

	 // use operator[] function
	 cout << s[0] << endl;
	 s[0] = 'A';
	 s.print();
	
	system("pause");
	return 0;
}

emmm,我之后回来修正,这里为什么报错了
报错内容:0x78DDFC66 (ucrtbased.dll) (Project2.exe 中)处有未经处理的异常: 将一个无效参数传递给了将无效参数视为严重错误的函数。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值