Rule of Three/Five/Zero

Rule of Three

If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, it almost certainly requires all three.

Rule of Five

Because the presence of a user-defined destructor, copy-constructor, or copy-assignment operator prevents implicit definition of the move constructor and the move assignment operator, any class for which move semantics are desirable, has to declare all five special member functions:

class string
{
    char* cstring;
public:
    //constructor
    string(const char* arg)
     : cstring(new char[std::strlen(arg) + 1 ]) {
         std::strcpy(cstring, arg);
         std::cout<<"constructor called"<<std::endl;
    }

    //No.1 destructor
    ~string(){
        delete[] cstring;
         std::cout<<"destructor called"<<std::endl;
    }

    //No.2 copy constructor
    string(const string& s)
     : cstring(new char[ s.size() + 1 ]){
         std::strcpy(cstring, s.cstring);
         std::cout<<"copy constructor called"<<std::endl;
    }

    //No.3 copy assignment constructor
    //tip 5 need to delete current cstring
    string operator=(const string& s)
    {
        char* tmp_str = new char[s.size() +1];
        std::strcpy(tmp_str, s.cstring);
        delete[] cstring;
        cstring = tmp_str;
         std::cout<<"copy assignment constructor called"<<std::endl;
        return *this;
    }

    //No.4 moveconstructor
    //tip 1 move not const parameter
    //tip 2 move constructor no need check self assignment
    string(string&& s) : cstring(s.cstring)
    {
        s.cstring = nullptr;
         std::cout<<"move constructor called"<<std::endl;
    }

    //No.5 move assignment constructor
    //tip 4 move assignment need check self assignment
    //tip 5 need to delete current cstring
    string operator=(string&& s)
    {
        if( &s != this) {
            char* tmp_str= s.cstring;
            s.cstring = nullptr;
            delete[] cstring;
            cstring = tmp_str;
        }
        std::cout<<"move assignment constructor called"<<std::endl;
return *this;

    }

    //others
    size_t size() const{
        return std::strlen(cstring);
    }

};
#include"string.h"
#include<iostream>
// tip 1 utility for std::move
#include<utility>

int main(){
    const char* a = "hello world";
    string s = string{a};
    string b = string{s};
    string c = string{ std::move(b) };
    string d{a};
    d = c;
    s = std::move(d);
    return 0;
}

output:
constructor called
copy constructor called
move constructor called
constructor called
copy assignment constructor called
copy constructor called
destructor called
move assignment constructor called
copy constructor called
destructor called
destructor called
destructor called
destructor called
destructor called

#include"string.h"
#include<iostream>
#include<utility>

int main(){
    const char* a = "hello world";
    string b  = string{a};
    std::move(b);
    std::cout<<"move called"<<std::endl;
    string c = string{a};
    c = b;
    return 0;
}
output:

constructor called
move called
constructor called
copy assignment constructor called
copy constructor called
destructor called
destructor called
destructor called

three destructor is due to when assign ment copy . called, b is copied first to argument,
that is why there is one line copy constructor called after copy assignment constructor called

Rule of zero

Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership (which follows from the Single Responsibility Principle). Other classes should not have custom destructors, copy/move constructors or copy/move assignment operators.[1]

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容