#include <iostream>
#include <string.h>
#include <vector>
using namespace std;//在class里面存在指针的时候,要使用深拷贝,此时注意三法则原则
class String
{
public:
String();
String(const char* s);
String(const String &s);
String &operator=(const String &s);
String &operator=(const char* s);
void print();
~String();
private:
char *str_;
};
String::String()
:str_(new char(0))//mark;
{}
String::String(const char*s)
:str_(new char[strlen(s) + 1])
{
strcpy(str_,s);
}
String::String(const String &s)
:str_(new char[strlen(s.str_) + 1])//分配空间
{
strcpy(str_,s.str_);
}
String &String::operator=(const String &s)
{
if (str_ != s.str_)//避免给自身赋值,造成delete掉自己的数据
{
delete [] str_;
str_ = new char[strlen(s.str_) + 1];
strcpy(str_,s.str_);
}
return *this;//处理方式,不要漏掉*
}
String &String::operator=(const char* s)
{
if (str_ != s)//避免给自身赋值,造成delete掉自己的数据
{
delete [] str_;
str_ = new char[strlen(s) + 1];
strcpy(str_,s);
}
return *this;
}
String::~String()
{
delete [] str_;
}
void String::print()
{
cout << str_ << endl;
}
int main(int argc, const char *argv[])
{
String s1("hello world!");
String s2,s3;
s2 = s3 = s1;
String s4(s1);
s1.print();
s2.print();
s3.print();
s4.print();
s2 = s2;
s2.print();
return 0;
}
mark
- 赋值操作符,需要先释放掉以前持有的资源。同时必须处理自身赋值的问题。
- 复制构造函数、赋值运算符以及析构函数,称为三法则,一旦提供了其中一个,务必提供其余两个。以String为例:
a) 涉及到深拷贝、浅拷贝问题,所以需要提供拷贝构造函数
b) 然后,为了保持一致,赋值运算符也应该实现深拷贝
c) 既然实现深拷贝,那么必定申请了资源(例如内存),所以必然需要析构函数来手工释放。
- 一个空类,编译器提供默认无参数构造函数、拷贝构造函数、赋值运算符以及析构函数,一共四个函数。
- 禁止一个类复制和赋值的方法:
a) 把copy构造函数和赋值运算符设为private
b) 只声明,不实现。
- 复制和赋值必须保证在程序的语义上具有一致性。
- 禁止类的复制和赋值:
#define DISALLOW_COPY_AND_ASSIGN(TypeName) TypeName(const TypeName&);
void operator=(const TypeName&)
- 如果一个类,不需要复制和赋值,那就禁用这种能力,这可以帮助避免大量潜在的bug。
- 如果一个类,实现了像value一样的复制和赋值能力(意味着复制和赋值后,两个对象没有任何关联,或者逻辑上看起来无任何关联),那么就称这个类的对象为值语义(value semantics)。如果类不能复制,或者复制后对象之间的资源归属纠缠不清,那么称为对象语义(object semantics),或者引用语义(reference semantics)。