【more effective c++读书笔记】【第5章】技术(5)——Reference counting(引用计数)(1)

一、非引用计数实现的String类

//String.h
#ifndef STRING_H
#define STRING_H
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>

class String{
public:
	String(const char* initValue = nullptr);//构造函数
	String(const String& rhs);//拷贝构造函数
	~String();//析构函数
	String& operator=(const String& rhs);//拷贝赋值运算符
	String operator+(const String& rhs);//重载+运算符
	String operator+=(const String& rhs);//重载+=运算符
	bool operator==(const String& rhs);//重载==运算符
	char& operator[](size_t index);//重载[]运算符
	int getLength();//获取长度
	friend std::istream& operator>>(std::istream& is, const String& str);//重载>>运算符
	friend std::ostream& operator<<(std::ostream& os, const String& str);//重载<<运算符
private:
	char* data;
};
//构造函数
String::String(const char* initValue){
	if (initValue == nullptr){
		data = new char[1];
		data[0] = '';
	}
	else{
		data = new char[strlen(initValue) + 1];
		strcpy(data, initValue);
	}
}
//拷贝构造函数,要new一块新内存
String::String(const String& rhs){
	this->data = new char[strlen(rhs.data) + 1];
	strcpy(this->data, rhs.data);
}
//析构函数
String::~String(){
	delete[] data;
	data = nullptr;
}
//拷贝赋值运算符,要new一块新内存
String& String::operator=(const String& rhs){
	if (this == &rhs)
		return *this;

	delete[] data;
	data = new char[strlen(rhs.data) + 1];
	strcpy(data, rhs.data);

	return *this;
}
//重载+运算符
String String::operator+(const String& rhs){
	String newStr;
	if (rhs.data == nullptr)
		newStr = *this;
	else if (this->data == nullptr)
		newStr = rhs;
	else{
		newStr.data = new char[strlen(this->data) + strlen(rhs.data) + 1];
		strcpy(newStr.data, this->data);
		strcat(newStr.data, rhs.data);
	}
	return newStr;
}
//重载+=运算符
String String::operator+=(const String& rhs){
	if (rhs.data == nullptr)
		return *this;
	char* pTemp = new char[strlen(this->data) + strlen(rhs.data) + 1];
	strcpy(pTemp, this->data);
	strcat(pTemp, rhs.data);
	delete[] this->data;
	this->data = pTemp;
	return *this;
}
//重载==运算符
bool String::operator==(const String& rhs){
	return strcmp(this->data, rhs.data) == 0 ? true : false;
}
//重载[]运算符
char& String::operator[](size_t index){
	if (index<strlen(data))
		return data[index];
}
//获取长度
int String::getLength(){
	return strlen(data);
}
//重载>>运算符
std::istream& operator>>(std::istream& is, const String& str){
	is >> str.data;
	return is;
}
//重载<<运算符
std::ostream& operator<<(std::ostream& os, const String& str){
	os << str.data;
	return os;
}
#endif
//main.cpp 
#include"String.h"
using namespace std;

int main(){
	String str1 = "hello";//调用构造函数
	String str2 = " world";//调用构造函数
	//调用重载+运算符
	String str3 = str1 + str2;
	cout << str3 << endl;//"hello world" 重载<<运算符
	//调用拷贝构造函数
	String str4 = str1;
	cout << str4 << endl;//"hello" 重载<<运算符
	//调用重载+=运算符
	str4 += str2;
	cout << str4 << endl;//"hello world" 重载<<运算符
	//调用重载==运算符
	if (str3 == str4)
		cout << "equal" << endl;//"equal" 
	//调用拷贝赋值运算符
	str4 = str2;
	cout << str4 << endl;//" world" 重载<<运算符

	cout << str4[2] << endl;//'o'

	String str5;
	cin >> str5; //重载>>运算符
	cout << str5 << endl;//重载>>运算符
	
	system("pause");
	return 0;
}

上述例子实现的是用非引用计数实现的String类,缺点是浪费内存,因为在拷贝构造函数和拷贝赋值运算符内都会new出一块新内存。

二、引用计数实现的String类

//String.h
#ifndef STRING_H
#define STRING_H
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>

class String{
public:
	String(const char* initValue = nullptr);//构造函数
	String(const String& rhs);//拷贝构造函数
	~String();//析构函数
	String& operator=(const String& rhs);//拷贝赋值运算符
	String operator+(const String& rhs);//重载+运算符
	String& operator+=(const String& rhs);//重载+=运算符
	bool operator==(const String& rhs);//重载==运算符
	const char& operator[](size_t index) const;//重载[]运算符,针对const Strings
	char& operator[](size_t index);//重载[]运算符,针对non-const Strings
	int getLength();//获取长度
	friend std::istream& operator>>(std::istream& is, const String& str);//重载>>运算符
	friend std::ostream& operator<<(std::ostream& os, const String& str);//重载<<运算符
	int getRefCount();//获取引用对象的个数
private:
	struct StringValue{
		int refCount;//引用计数
		char* data;
		StringValue(const char* initValue);//构造函数
		~StringValue();//析构函数
	};
	StringValue* value;
};
//StringValue类的构造函数
String::StringValue::StringValue(const char* initValue):refCount(1){
	if (initValue == nullptr){
		data = new char[1];
		data[0] = '';
	}
	else{
		data = new char[strlen(initValue) + 1];
		strcpy(data, initValue);
	}
}
//StringValue类的析构函数
String::StringValue::~StringValue(){
	delete[] data;
	data = nullptr;
}
//String类的构造函数
String::String(const char* initValue) :value(new StringValue(initValue)){}
//String类的拷贝构造函数
String::String(const String& rhs) : value(rhs.value){
	++value->refCount;//引用计数加1
}
//String类的析构函数
String::~String(){
	if (--value->refCount == 0){//析构时引用计数减1,当变为0时,没有指针指向该内存,销毁
		delete value;
	}
}
//String类的拷贝赋值运算符
String& String::operator=(const String& rhs){
	if (this->value == rhs.value) //自赋值
		return *this;
	//赋值时左操作数引用计数减1,当变为0时,没有指针指向该内存,销毁
	if (--value->refCount == 0)
		delete value;
	//不必开辟新内存空间,只要让指针指向同一块内存,并把该内存块的引用计数加1
	value = rhs.value;
	++value->refCount;
	return *this;
}
//String类的重载+运算符
String String::operator+(const String& rhs){
	return String(*this) += rhs;
}
//String类的重载+=运算符
String& String::operator+=(const String& rhs){
	//左操作数引用计数减1,当变为0时,没有指针指向该内存,销毁
	if (--value->refCount == 0)
		delete value;
	//右操作数为空
	if (rhs.value->data == nullptr){
		value = new StringValue(value->data);
		return *this;
	}	
	//左操作数为空
	if (this->value->data == nullptr){
		value = new StringValue(rhs.value->data);
		return *this;
	}
	//都不空
	char* pTemp = new char[strlen(this->value->data) + strlen(rhs.value->data) + 1];
	strcpy(pTemp, this->value->data);
	strcat(pTemp, rhs.value->data);
	value=new StringValue(pTemp);
	return *this;
}
//重载==运算符
bool String::operator==(const String& rhs){
	return strcmp(this->value->data, rhs.value->data) == 0 ? true : false;
}
//重载[]运算符,针对const Strings
const char& String::operator[](size_t index) const{
	if (index<strlen(value->data))
		return value->data[index];
}
//重载[]运算符,针对non-const Strings
char& String::operator[](size_t index){
	if (value->refCount>1){
		--value->refCount;
		value = new StringValue(value->data);
	}
	if (index<strlen(value->data))
		return value->data[index];
}
//获取长度
int String::getLength(){
	return strlen(this->value->data);
}
//重载>>运算符
std::istream& operator>>(std::istream& is, const String& str){
	is >> str.value->data;
	return is;
}
//重载<<运算符
std::ostream& operator<<(std::ostream& os, const String& str){
	os << str.value->data;
	return os;
}
//获取引用对象的个数
int String::getRefCount(){
	return value->refCount;
}

#endif
//main.cpp
#include"String.h"
using namespace std;

int main(){
	String str1("hello world");
	String str2 = str1;//调用拷贝构造函数
	String str3;//调用默认构造函数
	str3 = str2;//调用拷贝赋值运算符
	cout << "str1的引用计数是:" << str1.getRefCount() << endl; // 3
	cout << "str2的引用计数是:" << str2.getRefCount() << endl; // 3
	cout << "str3的引用计数是:" << str3.getRefCount() << endl; // 3

	str1[0] = 'H';//调用针对non-const Strings的重载[]运算符
	cout << str1 << endl; //"Hello world"
	cout << str2 << endl;//"hello world"
	cout << str3 << endl;//"hello world" 
	cout << "str1的引用计数是:" << str1.getRefCount() << endl;//1
	cout << "str2的引用计数是:" << str2.getRefCount() << endl;//2
	cout << "str3的引用计数是:" << str3.getRefCount() << endl;//2

	String str4("hello");//调用构造函数
	String str5 = str4;//调用拷贝构造函数
	String str6 = " world";//调用构造函数

    str5 = str5+str6;//调用String类的重载+运算符,调用String类的拷贝赋值运算符
	cout << str4 << endl; //"hello"
	cout << str5 << endl; //"hello world"
	cout << str6 << endl; //" world"
	cout << "str4的引用计数是:" << str4.getRefCount() << endl;//1
	cout << "str5的引用计数是:" << str5.getRefCount() << endl;//1
	cout << "str6的引用计数是:" << str6.getRefCount() << endl;//1

	String str7 = str5;//调用拷贝构造函数
	String str8;//调用默认构造函数
	str8 = str7;//调用String类的拷贝赋值运算符
	cout << str7 << endl; //"hello world"
	cout << "str5的引用计数是:" << str5.getRefCount() << endl;//3
	cout << "str7的引用计数是:" << str7.getRefCount() << endl;//3
	cout << "str8的引用计数是:" << str8.getRefCount() << endl;//3
	
	str5 += str6;//调用String类的重载+=运算符
	cout << str5 << endl; //"hello world world"
	cout << str6 << endl; //" world"
	cout << str7 << endl; //"hello world"
	cout << str8 << endl; //"hello world"
	cout << "str5的引用计数是:" << str5.getRefCount() << endl; //1
	cout << "str6的引用计数是:" << str6.getRefCount() << endl;//1
	cout << "str7的引用计数是:" << str7.getRefCount() << endl;//2
	cout << "str8的引用计数是:" << str8.getRefCount() << endl;//2
	
	system("pause");
	return 0;
}

引用计数允许多个等值对象共享同一实值。此计数有两个动机:第一为了简化堆对象周边的簿记工作。第二是为了实现一种常识,所有等值对象共享同一实值,不仅节省内存,也使程序速度加快。


版权声明:本文为博主原创文章,未经博主允许不得转载。

原文地址:https://www.cnblogs.com/ruan875417/p/4785416.html