简介
这里看class的另一个分类:class with pointer members.
先看String的声明
#ifndef _MYSTRING_
#define _MYSTRING_
class String
{
public:
String(const char* cstr = 0);
String(const String& str); //拷贝构造
String& operator = (const String& str); //拷贝赋值
~String();
char* get_c_str const () {return m_data};
private:
char* m_data;
};
#endif
如上所示:Big Three,三个特殊的函数:拷贝构造函数、拷贝赋值函数、析构函数。其实不写,编译器也会有默认的这些函数,那么什么时候需要写呢?就得看看编译器默认的这些函数够不够用。(默认的就是一个个位进行复制)
class with pointer members一般这样设计:指针作为成员变量,在需要内存的时候动态申请,而不是直接在class里放数组,因为不知道数组需要多大。显然得自己写Big three函数
class with pointer members 就一定要写构造函数、拷贝构造函数、析构函数
构造函数和析构函数
先看构造函数的实现
inline String::String(const char* cstr = 0)
{
if (cstr)
{
m_data = new char[strlen(cstr) + 1];
strcpy(m_data,cstr);
}
else //未指定值,形成空字符串
{
m_data = new char[1];
*m_data = '\0';
}
}
析构函数需要清理空间,这里的class有动态申请空间,若没有释放掉的话,会有内存泄露
inline String::~String()
{
delete [] m_data;
}
拷贝构造函数
如上String的设计,使用如下
String a("Hello");
String b("World");
b = a;
String类只包含m_data指针,并不包含其申请的动态空间,
如果使用默认拷贝构造函数,编译器会自己一个字节一个字节的复制,会出现的y严重的后果。如果把a的内容复制到b中去,就会出现a的m_data和b的m_data指向一个地方,那么b指向的内容”World”就没有指针指向他了,会出现内存泄露。而赋值后两个指针指向一块内存也很危险:你更改a,b就会受影响,所以这种叫浅拷贝,只会拷贝指针。会有两个隐患。(见下示意图)
改进方法就是使用深拷贝,如下
要去写的就是深拷贝。
先看拷贝构造函数,如下
String::String(const String& str)
{
m_data = new char[ strlen(str.m_data + 1) ];
strcpy(m_data,str.m_data);
}
使用如下
String s1("Hello");
String s2(s1); //以s1为蓝本、初值创建s2
String s3 = s1; //调用拷贝构造函数 //把s1赋值到s3上,但s3也是新创建的对象(既然新创建的就要调用构造函数)。
拷贝赋值
使用如下
b = a;
首先明白:b和a对象都有内容,那么把b赋值给a,需要以下三个步骤
三步骤:先清空b的内容,再为b分配一个空间,然后再将a的内容拷贝过来给b。
下面看函数
inline String& String::operator = (const String& str)
{
if (this == &str) //检测自我赋值
return *this;
delete[] m_data;
m_data = new char[ strlen(str.m_data + 1)];
strcpy(m_data,str.m_data);
return *this;
}
注意:自我赋值检测的必要性,谁会做自我赋值的动作呢?所以说这样效率高,
如果没有写这两行,不仅仅是效率问题。如果没有这两行,可以试试赋值自己会出现什么bug。所以我自我赋值的检测是必要的
再看一个程序,看下什么时候调用 big three 函数
String str1("hello"); //调用构造函数
String str2(str1); //调用拷贝构造函数
String str3 = str1; //调用拷贝构造函数
str3 = str2; //调用拷贝赋值函数
先给出String的完整程序
#ifndef __MYSTRING__
#define __MYSTRING__
class String
{
public:
String(const char* cstr=0);
String(const String& str);
String& operator=(const String& str);
~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)
{
if (this == &str)
return *this;
delete[] m_data;
m_data = new char[ strlen(str.m_data) + 1 ];
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;
ostream& operator<<(ostream& os, const String& str)
{
os << str.get_c_str();
return os;
}
#endif
本文详细介绍了C++类中如何实现深度拷贝和浅度拷贝,包括构造函数、拷贝构造函数、析构函数及拷贝赋值运算符的实现,以及内存管理的重要性。
852

被折叠的 条评论
为什么被折叠?



