c++ 之“Big Three”

c++ 之 “Big Three”

最新版本:https://blog.csdn.net/qq_62357480/article/details/129151139?spm=1001.2014.3001.5501

简介

什么是Big Three?

三法则(英语:rule of three,the Law of The Big Three,The Big Three;三法则,三大定律)在 C++ 程序设计里,它是一个以设计的基本原则而制定的定律,三法则的要求在于,假如类有明显地定义下列其中一个成员函数,那么程序员必须连其他二个成员函数也一同编写至类内,亦即下列三个成员函数缺一不可:

析构函数(Destructor)
复制构造函数(copy constructor)
复制赋值运算符(copy assignment operator)
上述三个函数是特别的成员函数,假如程序员没有自行定义或是编写声明它们,那么编译器会自动地创建它们,并且会编译至应用程序内。相反地,假如程序员有定义上述三者其中一个函数,那么由编译器自动产生出来的上述三个函数是不会搭配到这个类内。三法则(Rule of three)这个专有名词是由 Marshall Cline 于 1991 年创立的。

简单举例

那么为什么要这么做呢?当数据成员有管理动态开辟内存的指针时,需要使用该原则。取其一来说:

当一个class具有一个指针类型成员时,如下:

class String{
  private char* str;
}

当他进行以下几个操作的时候:

String s = "hello,world!";
String s1(s);
String s2 = s;

第一个方法就是一个显示的调用拷贝构造函数,我们可以很简单的写出对应的(当然这也是必要的):

String::String(const char * cstr)
{
    // code
}

但是当涉及到后面两个的做法时,第二个是一个拷贝构造的方式创建对象,第三个是一个拷贝赋值的方式创建对象。如果我们自己没有定义相对应自己的class的这两个函数时,编译器会给我们自动生成一个操作该步骤的函数,这是一个逐个字节拷贝的过程,将s中的每一个字节逐个复制给s1和s2。这样的做法有一个坏处就是,当class中存在指针类型或者需要动态分配内存的成员变量时,很大可能上会出现悬垂指针

什么是悬垂指针,假设如下:

String s = "hello,world!";
String s1(s);

s 和 s1都属于一个char类型的指针,当你没有给出对应的拷贝构造和拷贝赋值函数时,进行如上操作时,对s进行每一个字节的复制,那么传给s1和s2的就都是指向 “hello,world” 的指针,假设s在离开作用域后被自动回收delete,但是s1因为其他操作生命周期并没有结束,那么s在delete时已经将 “hello,world” 的内存释放了,那么现在的s1指向已经消失了,变成了一个悬垂指针。(野指针和悬垂指针的区别就是,悬垂指针是原先存在指向内存但是指向内存被释放了,而野指针一直没有指向的内存,未经初始化的指针。)

悬垂指针和野指针的存在都是非常危险的,很可能就会引起程序的崩溃。

简单应用

根据侯捷老师的课程,列举出一个较为简单的Big There函数

  • class定义
//三个特殊函数:拷贝构造函数    拷贝赋值函数    析构函数 
//big three : 这是三个极其重要的函数
#include
#include 
class String
{
private:
    char * m_data;


public:
    String (const char * cstr = 0);
    String (const String & str);        			//接受一个像自己一样的对象称为拷贝构造,如果不给出,编译器会自动添加一个。 -- 拷贝构造函数
    String & operator = (const String & str);       //拷贝赋值在 = 操作符的重载中实现 -- 拷贝赋值函数
    ~String ();         							//析构函数
    char* get_c_str() const { return m_data; };     //返回字符串指针的成员函数
};
  • 构造函数
// 构造函数
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';								//已结束符号为终止符
    }
}
  • 析构函数
// destructor
inline
String::~String()
{
    delete[] m_data;							    //这是属于动态分配的内存
}
  • 拷贝构造函数
// 拷贝构造函数
inline 
String::String (const String& str)
{
    m_data = new char[ strlen(str.m_data) + 1];
    strcpy(m_data, str.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;
}

如有错误欢迎指正以及评论区交流!

你可能感兴趣的:(笔记,c++,算法,开发语言)