gpt4 book ai didi

c++ - 三分法则是什么?

转载 作者:行者123 更新时间:2023-12-01 16:04:35 25 4
gpt4 key购买 nike

  • 复制对象是什么意思?
  • 什么是复制构造函数和复制赋值运算符?
  • 我什么时候需要自己申报?
  • 如何防止我的对象被复制?
  • 最佳答案

    介绍
    C++ 使用值语义处理用户定义类型的变量。
    这意味着对象在各种上下文中被隐式复制,
    我们应该理解“复制对象”的实际含义。
    让我们考虑一个简单的例子:

    class person
    {
    std::string name;
    int age;

    public:

    person(const std::string& name, int age) : name(name), age(age)
    {
    }
    };

    int main()
    {
    person a("Bjarne Stroustrup", 60);
    person b(a); // What happens here?
    b = a; // And here?
    }
    (如果您对 name(name), age(age) 部分感到困惑,
    这称为 member initializer list .)
    特殊成员函数
    复制 person 是什么意思目的? main函数显示了两种不同的复制场景。
    初始化 person b(a);由复制构造函数执行。
    它的工作是根据现有对象的状态构造一个新对象。
    任务 b = a由复制赋值运算符执行。
    它的工作通常有点复杂,
    因为目标对象已经处于某种需要处理的有效状态。
    由于我们自己既没有声明复制构造函数也没有声明赋值运算符(也没有声明析构函数),
    这些是为我们隐式定义的。引用标准:

    The [...] copy constructor and copy assignment operator, [...] and destructor are special member functions.[ Note: The implementation will implicitly declare these member functionsfor some class types when the program does not explicitly declare them.The implementation will implicitly define them if they are used. [...] end note ][n3126.pdf section 12 §1]


    默认情况下,复制对象意味着复制其成员:

    The implicitly-defined copy constructor for a non-union class X performs a memberwise copy of its subobjects.[n3126.pdf section 12.8 §16]


    The implicitly-defined copy assignment operator for a non-union class X performs memberwise copy assignmentof its subobjects.[n3126.pdf section 12.8 §30]


    隐含定义 person 的隐式定义的特殊成员函数看起来像这样:
    // 1. copy constructor
    person(const person& that) : name(that.name), age(that.age)
    {
    }

    // 2. copy assignment operator
    person& operator=(const person& that)
    {
    name = that.name;
    age = that.age;
    return *this;
    }

    // 3. destructor
    ~person()
    {
    }
    在这种情况下,成员复制正是我们想要的: nameage被复制,所以我们得到一个独立的,独立的 person目的。
    隐式定义的析构函数始终为空。
    在这种情况下这也很好,因为我们没有在构造函数中获取任何资源。
    成员的析构函数在 person 之后被隐式调用析构函数完成:

    After executing the body of the destructor and destroying any automatic objects allocated within the body,a destructor for class X calls the destructors for X's direct [...] members[n3126.pdf 12.4 §6]


    管理资源
    那么我们什么时候应该显式声明那些特殊的成员函数呢?
    当我们的类(class)管理一个资源时,也就是
    当类的对象负责该资源时。
    这通常意味着资源是在构造函数中获取的
    (或传入构造函数)并在析构函数中释放。
    让我们回到标准之前的 C++。
    没有 std::string 这样的东西,而程序员则爱上了指针。 person类可能看起来像这样:
    class person
    {
    char* name;
    int age;

    public:

    // the constructor acquires a resource:
    // in this case, dynamic memory obtained via new[]
    person(const char* the_name, int the_age)
    {
    name = new char[strlen(the_name) + 1];
    strcpy(name, the_name);
    age = the_age;
    }

    // the destructor must release this resource via delete[]
    ~person()
    {
    delete[] name;
    }
    };
    即使在今天,人们仍然以这种方式编写类并陷入困境:
    “我将一个人插入了一个 vector ,现在我出现了疯狂的内存错误!”
    请记住,默认情况下,复制对象意味着复制其成员,
    但复制 name成员只是复制一个指针,而不是它指向的字符数组!
    这有几个令人不快的影响:
  • 通过 a 更改可以通过 b 观察.
  • 曾经b被摧毁,a.name是一个悬空指针。
  • a被破坏,删除悬空指针产生 undefined behavior .
  • 由于赋值没有考虑到什么name在分配之前指出,
    迟早你会到处都是内存泄漏。

  • 明确的定义
    由于按成员复制没有达到预期的效果,我们必须明确定义复制构造函数和复制赋值运算符,以对字符数组进行深度复制:
    // 1. copy constructor
    person(const person& that)
    {
    name = new char[strlen(that.name) + 1];
    strcpy(name, that.name);
    age = that.age;
    }

    // 2. copy assignment operator
    person& operator=(const person& that)
    {
    if (this != &that)
    {
    delete[] name;
    // This is a dangerous point in the flow of execution!
    // We have temporarily invalidated the class invariants,
    // and the next statement might throw an exception,
    // leaving the object in an invalid state :(
    name = new char[strlen(that.name) + 1];
    strcpy(name, that.name);
    age = that.age;
    }
    return *this;
    }
    注意初始化和赋值的区别:
    我们必须在分配给 name 之前拆除旧状态以防止内存泄漏。
    此外,我们必须防止表单 x = x 的自赋值。 .
    如果没有那张支票, delete[] name将删除包含源字符串的数组,
    因为当你写 x = x , 两者 this->namethat.name包含相同的指针。
    异常安全
    不幸的是,如果 new char[...],此解决方案将失败由于内存耗尽而引发异常。
    一种可能的解决方案是引入一个局部变量并对语句重新排序:
    // 2. copy assignment operator
    person& operator=(const person& that)
    {
    char* local_name = new char[strlen(that.name) + 1];
    // If the above statement throws,
    // the object is still in the same state as before.
    // None of the following statements will throw an exception :)
    strcpy(local_name, that.name);
    delete[] name;
    name = local_name;
    age = that.age;
    return *this;
    }
    这也可以在没有明确检查的情况下处理自分配。
    对此问题的更强大的解决方案是 copy-and-swap idiom ,
    但我不会在这里讨论异常安全的细节。
    我只提到了异常(exception)来说明以下几点: 编写管理资源的类很困难。
    不可复制的资源
    某些资源不能或不应被复制,例如文件句柄或互斥锁。
    在这种情况下,只需将复制构造函数和复制赋值运算符声明为 private没有给出定义:
    private:

    person(const person& that);
    person& operator=(const person& that);
    或者,您可以从 boost::noncopyable 继承或将它们声明为已删除(在 C++11 及更高版本中):
    person(const person& that) = delete;
    person& operator=(const person& that) = delete;
    规则三
    有时您需要实现一个管理资源的类。
    (永远不要在一个类中管理多个资源,
    这只会导致疼痛。)
    在这种情况下,请记住 三规则 :

    If you need to explicitly declare either the destructor,copy constructor or copy assignment operator yourself,you probably need to explicitly declare all three of them.


    (不幸的是,这个“规则”不是由 C++ 标准或我知道的任何编译器强制执行的。)
    五分法则
    从 C++11 开始,一个对象有 2 个额外的特殊成员函数:移动构造函数和移动赋值。五国之治也执行这些功能。
    带有签名的示例:
    class person
    {
    std::string name;
    int age;

    public:
    person(const std::string& name, int age); // Ctor
    person(const person &) = default; // 1/5: Copy Ctor
    person(person &&) noexcept = default; // 4/5: Move Ctor
    person& operator=(const person &) = default; // 2/5: Copy Assignment
    person& operator=(person &&) noexcept = default; // 5/5: Move Assignment
    ~person() noexcept = default; // 3/5: Dtor
    };
    零的法则
    3/5 规则也称为 0/3/5 规则。规则的零部分声明在创建类时不允许编写任何特殊成员函数。
    建议
    大多数情况下,您不需要自己管理资源,
    因为一个现有的类,例如 std::string已经为你做了。
    只需使用 std::string 比较简单的代码成员
    到使用 char* 的复杂且容易出错的替代方案你应该被说服。
    只要您远离原始指针成员,三原则就不太可能涉及您自己的代码。

    关于c++ - 三分法则是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4172722/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com