C++基于对象--Class with pointer--string类的简单实现

本文所谈的对象是带有指针的类(Class with pointer);这时不能使用编译器自动合成的三大函数(Big three):拷贝构造、拷贝赋值和析构需要自己去显式的定义着三大函数;

primer中给出的建议是:如果一个类需要需要自定义析构函数,几乎可以肯定它也需要自定义拷贝赋值运算符和拷贝构造函数。

所以一般情况下,这三个函数总是一起自定义,或者一起不定义

在实际中,如果定义了其他构造函数,那么最好也提供一个默认构造函数。

 

1、拷贝构造函数

定义:如果一个构造函数的第一个参数是自身类类型的引用(必须是引用),且任何额外参数都有默认值,则此构造函数是拷贝构造函数。

并且参数几乎总是const的引用

对于不含指针的类,合成拷贝构造函数和自定义的拷贝构造函数等价;

 

拷贝初始化不仅发生在用=定义变量时,下列情况也会发生:

(1)将一个对象作为实参传递给一个非引用类型的形参。

(2)从一个返回类型为非引用类型的函数返回一个对象。

(3)用花括号列表初始化一个数组中的元素或聚合类中的成员。

 

对于编译器自动生成的合成拷贝构造函数,依次拷贝每个成员变量--copies each member varible(nembers,objects,arrays)。

一旦成员里面有指针,也会拷贝指针,造成的结果就是两个指针指向同一片内存。最终导致类的析构失败。

 

2、拷贝赋值运算符

拷贝赋值其实就是对赋值运算符重载。

对于赋值运算符,必须定义为成员函数。

赋值运算符通常返回一个指向类对象的引用。

 

3、析构函数

析构函数释放对象使用的资源,并销毁对象的非static数据成员。

隐式销毁一个内置指针类型的成员不会delete它所指向的对象。

 

下面的程序来自于--侯捷老师的网络课程《C++面向对象高级开发》

通过程序详细的介绍Class with pointer的设计。

 

string.h

#ifndef __MYSTRING__
#define __MYSTRING__
#define _CRT_SECURE_NO_WARNINGS
class String
{
public:
	//默认构造函数,并设置默认值
	String(const char* cstr = 0);

	//Big Three三大函数:拷贝构造,拷贝赋值,析构。编译器会自动给出三大函数。
	//对于class with pointer,三大函数需要自己写,不能使用编译器给的默认版本。
	//不然会出现内存泄漏(memory leak)和别名(alias):多个指针指向同一块内存。

	//1. 拷贝构造
	String(const String& str);
	//2. 拷贝赋值,返回值为String&,可以满足连串赋值的操作
	String& operator=(const String& str);
	//3. 析构
	~String();

	char* get_c_str() const { return m_data; }
private:
	char* m_data;
};

#include <cstring>

inline
String::String(const char* cstr)
{
	//对传入的指针进行判断
	if (cstr) {
		m_data = new char[strlen(cstr) + 1];
		strcpy(m_data, cstr);
	}
	//若未指定初值
	else { 
		m_data = new char[1]; //为了搭配上面的写法
		*m_data = '\0';
	}
}

inline
String::~String()
{
	delete[] m_data;
}

inline
String& String::operator=(const String& str)
{
	//检查自我赋值(self assignment)
	//1. 提高效率
	//2. 正确性,如果自我赋值会出错
	if (this == &str)
		return *this;

	//经典写法1-2-3
	//1. 先清空自己指向的内存
	delete[] m_data;  
	//2. 分配需要的内存
	m_data = new char[strlen(str.m_data) + 1];
	//3. 最后拷贝数据
	strcpy(m_data, str.m_data);
	return *this;
}

//深拷贝,编译器自动给出的为浅拷贝
inline
String::String(const String& str)
{
	m_data = new char[strlen(str.m_data) + 1];
	strcpy(m_data, str.m_data);
}

#include <iostream>
using namespace std;

// <<的重载必须写为非成员函数
// 如果写成成员函数,使用者则需要这样写:string << cout; 这样不符合常用习惯。
ostream& operator<<(ostream& os, const String& str)
{
	os << str.get_c_str();
	return os;
}

#endif


string-test.cpp

#include "string.h"

int main()
{
	String str1;
	String str2("hello str2");//默认构造
	String str3 = "hello str3";//默认构造

	String str4(str2);//拷贝构造
	String str5 = str4;//拷贝构造,同上

	str1 = str2;//赋值构造

	//重载<<
	cout << "str1:" << str1 << endl;
	cout << "str2:" << str2 << endl;
	cout << "str3:" << str3 << endl;
	cout << "str4:" << str4 << endl;
	cout << "str5:" << str5 << endl;

	//new:先分配memory,再调用ctor。
	String* pstr = new String[3];
	//编译器将上面语句翻译为下面三个动作
	//1. void* mem = operator new(sizeof(String)*3); 其内部调用malloc(n);
	//2. pc = static_cast<String*>(mem);
	//3. pc->String::String();调用三次构造

	pstr[0] = str1;
	pstr[1] = str2;
	pstr[2] = str3;
	cout << "pstr[1]:" << pstr[0] << endl;
	cout << "pstr[2]:" << pstr[1] << endl;
	cout << "pstr[3]:" << pstr[2] << endl;

	//delete:先调用dtor,再释放memory;
	delete[] pstr;
	//编译器将上面语句翻译为下面两个动作
	//1. String::~String(pstr);调用三次析构。
	//2. operator delete(pstr);  其内部调用free(pstr);
}

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章