Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/133.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用Init()进行内存分配比使用构造函数更好吗?_C++_Exception_Memory Management_Constructor - Fatal编程技术网

C++ 使用Init()进行内存分配比使用构造函数更好吗?

C++ 使用Init()进行内存分配比使用构造函数更好吗?,c++,exception,memory-management,constructor,C++,Exception,Memory Management,Constructor,假设我有这样的东西: class obj001 { public: obj001() { std::cout << "ctor == obj001" << std::endl; } ~obj001() { std::cout << "dtor == obj001" << std::endl; } }; class obj002 { public: obj002() {

假设我有这样的东西:

class obj001
{
public:
    obj001() {
        std::cout << "ctor == obj001" << std::endl;
    }

    ~obj001() {
        std::cout << "dtor == obj001" << std::endl;
    }
};

class obj002
{
public:
    obj002() {
        std::cout << "ctor == obj002" << std::endl;
    }

    ~obj002() {
        std::cout << "dtor == obj002" << std::endl;
    }
};

class packet001
{
public:
    packet001(): p01(NULL), p02(NULL) {
        /*p01 = new obj001;
        p02 = new obj002;
        throw "hahaha";*/

        std::cout << "CTOR == PACKET01" << std::endl;
    }

    ~packet001() {
        delete p01;
        delete p02;

        std::cout << "DTOR == PACKET01" << std::endl;
    }

    void init() {
        p01 = new obj001;
        p02 = new obj002;
        throw "hahaha";
    }

    obj001* p01;
    obj002* p02;
};
然后
init()
失败,将调用
superpack
的Dtor

但是如果我将内存分配放在
超级包的Ctor中,
(当然,不要执行
init()

然后在Ctor失败后,将不会调用Dtor,因此
p01
p02
会泄漏

因此,最好使用
init()


谢谢

最好的办法是完全避免此类分配。对于许多事情,可以将实例直接放在类中。如果确实需要指针,可以使用unique_ptr和shared_ptr进行自动内存管理

在您的示例中,这很好:

struct packet001
{
    obj001 p01;
    obj002 p02;
};
如果需要它们作为指针:

struct packet001
{
    packet001()
      : p01(new obj001),
        p02(new obj002)
    {
    }

    std::unique_ptr<obj001> p01;
    std::unique_ptr<obj002> p02;
};
struct packet001
{
packet001()
:p01(新obj001),
p02(新obj002)
{
}
std::唯一的ptr p01;
std::唯一的ptr p02;
};

析构函数中的内存将自动释放,如果在构造过程中发生异常,则释放将正常进行。

最好的办法是完全避免此类分配。对于许多事情,可以将实例直接放在类中。如果确实需要指针,可以使用unique_ptr和shared_ptr进行自动内存管理

在您的示例中,这很好:

struct packet001
{
    obj001 p01;
    obj002 p02;
};
如果需要它们作为指针:

struct packet001
{
    packet001()
      : p01(new obj001),
        p02(new obj002)
    {
    }

    std::unique_ptr<obj001> p01;
    std::unique_ptr<obj002> p02;
};
struct packet001
{
packet001()
:p01(新obj001),
p02(新obj002)
{
}
std::唯一的ptr p01;
std::唯一的ptr p02;
};

内存将在析构函数中自动释放,如果在构造过程中发生异常,则释放将正确发生。

您是否应该捕获Ctor中的所有异常,并在异常到达Ctor内部时正确清理?

您是否应该捕获Ctor中的所有异常,并在异常到达Ctor内部时正确清理?

使用两阶段构造,普通构造之后是对
init
函数的外部调用,这意味着构造之后您还不知道手头是否有有效的对象。这就意味着,在任何函数中,只要有一个对象作为参数,你就不知道这个对象是否有效。这意味着大量额外的检查和不确定性,这反过来意味着bug和额外的工作,因此,构造函数应该建立一个功能齐全的有效对象

进入“功能,有效”概念的一组假设称为类不变量

换句话说,更学术的措辞,构造器的工作是建立类不变量,这样在构造之后它就保持不变了

然后在每个外部可用的操作中保持对象有效,意味着它将继续被保证有效。因此,不需要进一步的有效性检查。该方案并非完全适用于所有对象(反例是一个表示文件的对象,其中任何操作都可能导致该对象实际上无效),但大多数情况下,它是一个好主意,效果很好,如果它不能直接工作,则适用于部分

因此,在构造函数中,您应该通过以下方式之一确保清理:

  • 使用标准库容器(或第三方容器),而不是直接处理原始数组和动态分配

  • 或者使用每个仅管理一个资源的子对象。子对象可以是数据成员或基类。如果是数据成员,则它可以是智能指针

  • 或者在最坏的情况下,使用
    try
    -
    catch
    进行直接清理

在技术上也可以使用检查返回值的C思想在必要时调用直接清理。但是上面的列表是为了减少易用性和安全性。C风格的编码在该列表的底部之外


<> P> C++语言创作者Bjarne Stroustrup在他的附录中写了一点关于这个主题的文章。只需下载PDF,然后在PDF阅读器中搜索“init”(“。您应该直接进入§E3.5节,关于构造函数和不变量;请至少阅读§E.3.5.1节,关于使用init()函数

正如比亚恩在那里列出的那样

[…]拥有一个单独的
init()
函数是实现
[1] 忘记调用
init()
(§10.2.3),
[2] 忘记测试
init()

[3] 多次调用
init()

[4] 忘记
init()
可能引发异常,然后
[5] 在调用
init()
之前使用该对象

我认为,比亚恩的讨论对初学者来说是很好的,整本书也是如此


<>但是,注意到两个构造的共同原因,即支持<强C++派生类初始化< <强> >,根本没有提到,这里不是Bjarne的图片的一部分。这是许多GUI框架中两阶段初始化的原因。然而,XIST证明,大多数席上都是一个教育问题——那些早期的C++程序员根本不知道,或者不能假设他们的图书馆用户会理解,C++ +RAII.< /P> < P>使用<强>两阶段构造>普通构造,接着调用一个<代码> init < /Cuff>函数,即THA。在构造之后,你还不知道手头是否有一个有效的对象。这意味着在任何以参数形式获取对象的函数中,你都不知道该对象是否有效。这意味着大量额外的检查和不确定性,这反过来意味着bug和额外的工作,因此,构造函数应该建立一个完整的函数nal,有效对象

那个