为什么C ++ 11引入委托构造函数?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (33)

我无法理解委托构造函数的用途。

它可以做这样简单的事情

class M 
{
 int x, y;
 char *p;
public:
 M(int v) : x(v), y(0), p(new char [MAX]) {}
 M(): M(0) {cout<<"delegating ctor"<<endl;}
};

但我不认为值得为这样一个简单的东西引入一个新功能?

提问于
用户回答回答于

委托构造函数防止代码重复

例子:

1)N1986建议的通用初始化:

class X { 
 X( int, W& ); 
 Y y_; 
 Z z_; 
public: 
 X(); 
 X( int ); 
 X( W& ); 
}; 
X::X( int i, W& e ) : y_(i), z_(e) { /*Common Init*/ } 
X::X() : X( 42, 3.14 )             { SomePostInitialization(); } 
X::X( int i ) : X( i, 3.14 )       { OtherPostInitialization(); } 
X::X( W& w ) : X( 53, w )          { /* no post-init */ } 

2)同时来自N1986提案的构造函数和拷贝构造函数:

class FullName { 
 string firstName_; 
 string middleName_; 
 string lastName_; 

public: 
 FullName(string firstName, string middleName, string lastName); 
 FullName(string firstName, string lastName); 
 FullName(const FullName& name); 
}; 
FullName::FullName(string firstName, string middleName, string lastName) 
 : firstName_(firstName), middleName_(middleName), lastName_(lastName) 
{ 
 // ... 
} 
// delegating copy constructor 
FullName::FullName(const FullName& name) 
 : FullName(name.firstName_, name.middleName_, name.lastName_) 
{ 
 // ... 
} 
// delegating constructor 
FullName::FullName(string firstName, string lastName) 
 : FullName(firstName, "", lastName) 
{ 
 // ... 
} 

3)构造函数执行参数验证:

class class_c {
public:
    int max;
    int min;
    int middle;

    class_c() {}
    class_c(int my_max) { 
        max = my_max > 0 ? my_max : 10; 
    }
    class_c(int my_max, int my_min) { 
        max = my_max > 0 ? my_max : 10;
        min = my_min > 0 && my_min < max ? my_min : 1;
    }
    class_c(int my_max, int my_min, int my_middle) {
        max = my_max > 0 ? my_max : 10;
        min = my_min > 0 && my_min < max ? my_min : 1;
        middle = my_middle < max && my_middle > min ? my_middle : 5;
    }
};

用户回答回答于

一个例子,我使用简单的原始指针。

#include <iostream>

class X
{
public:
    X()
    {
        std::cout << "X()\n";
    }

    ~X()
    {
        std::cout << "~X()\n";
    }

    X(const X&)
    {
        std::cout << "X(const&)\n";
    }

    X& operator=(const X&) = delete;
};

class Y
{
public:
    Y()
    {
        std::cout << "Y()\n";
    }

    ~Y()
    {
        std::cout << "~Y()\n";
    }

    Y(const Y&)
    {
        throw 1;
    }

    Y& operator=(const Y&) = delete;
};

现在,演示类Z包含一个手动管理的指向a X和a的指针Y

class Z
{
    X* x_ptr;
    Y* y_ptr;
public:
    Z()
        : x_ptr(nullptr)
        , y_ptr(nullptr)
    {}

    ~Z()
    {
        delete x_ptr;
        delete y_ptr;
    }

    Z(const X& x, const Y& y)
        : x_ptr(new X(x))
        , y_ptr(new Y(y))
        {}
};

Z(const X& x, const Y& y)它所依赖的构造函数并不是特例安全的。展示:

int
main()
{
    try
    {
        Z z{X{}, Y{}};
    }
    catch (...)
    {
    }
}

输出:

X()
Y()
X(const&)
~Y()
~X()

有几种方法可以使这个构造函数安全,一种方法是:

Z(const X& x, const Y& y)
    : x_ptr(new X(x))
    , y_ptr(nullptr)
{
    try
    {
        y_ptr = new Y(y);
    }
    catch (...)
    {
        delete x_ptr;
        throw;
    }
}

示例程序现在可以正确输出:

X()
Y()
X(const&)
~X()
~Y()
~X()

然而,人们可以很容易地看到,当你添加托管资源时Z,这很快就会变得麻烦。这个问题通过委托构造函数非常优雅地解决:

Z(const X& x, const Y& y)
    : Z()
{
    x_ptr = new X(x);
    y_ptr = new Y(y);
}

该构造函数首先委托给默认的构造函数,该构造函数除了将类放入有效的无资源状态之外什么都不做。一旦默认的构造函数完成,Z现在被认为是完全构造的。因此,如果此构造函数的任何内容抛出,~Z()现在运行(与前面的示例实现不同),Z(const X& x, const Y& y)~Z()正确清理已构建的资源(并忽略那些未构建的资源)。

如果你必须编写一个在其析构函数中管理多个资源的类,并且由于某种原因你不能使用其他对象来管理这些资源(例如unique_ptr),我强烈建议这个习惯用法来管理异常安全。

扫码关注云+社区

领取腾讯云代金券