#include <iostream>
#include <cstring>
using namespace std;
class Str
{
private:
static int num; // 实例计数器
char *str;
public:
Str(){str=0;} // 指向0,防止delete时程序崩溃!
Str(char *s) // 普通构造函数
{
num ++;
str = new char[strlen(s)+1];
strcpy(str, s);
}
// 深复制 复制构造函数
Str(Str &s)
{
num ++;
str = new char[strlen(s.str)+1];
strcpy(str, s.str);
}
// 深复制 赋值运算符
Str& operator=(Str &s)
{
if (&s==this) // 判断是不是赋值给自己(很重要!)
return *this;
num ++;
delete[] str; // 释放之前str指向的内存(很重要!因此在构造函数中要将str指向某个确定的内存,如果没有,会造成崩溃)
str = new char[strlen(s.str)+1];
strcpy(str, s.str);
return *this;
}
// 返回引用使得可以通过a[1]='a'修改某个字符的值
char& operator[](int i)
{
return str[i];
}
// 只返回值,不允许修改数据
char operator[](int i) const
{
return str[i];
}
void show()
{
cout<<num<<" string: "<<str<<" 地址:"<<(int*)str<<endl;
}
~Str()
{
cout<<endl;
cout<<num<<" 析构: "<<str<<" 地址:"<<(int*)str<<endl;
num--;
delete[] str;
}
};
int Str::num=0;
int main()
{
if (1)
{
char* ss = "some string";
Str q(ss);
q.show();
// 复制构造函数
Str w = q; // Str w(q); Str w = Str(q); Str *w = new Str(q);
w.show();
// 赋值运算符
Str e;
e = q;
e.show();
// 赋值给自己则直接返回自己
e = e;
e.show();
// 重载[] 实现修改字符
e[0] = 'e';
e.show();
// 对于const对象,必须定义[] const方法,否则无法使用[]
const Str r("duck");
// 如果没有 char operator[](int i) const, 则无法使用[],因为编译器不知道[]会不会修改r
cout<<"r[0] = "<<r[0]<<endl;
}
cin.get();
}
运行结果如下:
几点说明如下:
- 类的默认 复制构造函数 和 赋值运算符 都是浅复制,即直接复制成员中的值(包括普通值与地址(指针))
- 在类的成员中,如果没有使用指针,则浅复制完全可行!
- 类的成员中一旦含有指针,则必须自行定义复制构造函数,以实现深复制
- 类的静态成员只能在类外定义,以 int A::a=1; 的形式,注意前面不能加 static;只有类内申明时才需要加 static,相同原理的还有友元函数的 friend 标识。
- 注意 A a=b; 和 A a; a= b; 的区别:前者调用 复制构造函数 ,后者调用 赋值运算符,因此如需深复制,最好将2个都重写!!!
- 其他语言细节详见代码中的注释
下图说明了深复制和浅复制的区别:(图片来自c++ primer plus)