返回具有2个可选ctors的对象

时间:2018-05-31 15:34:26

标签: c++ function object constructor return

我在c ++中有一个函数,它有一个我构建的对象的2个可选c'tor(一个带有向量“vals”和其他没有的东西)。

...
    RecievedMessage a(sc, type);
    if (!vals.empty()){
        //a.~RecievedMessage();
        RecievedMessage a(sc, type, vals);
    }
    return &a;
}

//中的行是可选的。

是否有效(有或没有可选线)?为什么?如果不是,如何在没有“vals”的setter的情况下修复它? 非常感谢。

3 个答案:

答案 0 :(得分:3)

不,它不起作用。

    RecievedMessage a(sc, type);
// Here we construct 'a'
    if (!vals.empty()){
        //a.~RecievedMessage();
// If we enable this line, we destroy 'a'
        RecievedMessage a(sc, type, vals);
// Here we construct a second 'a' that only exists in this block
    }
// End of block: The inner 'a' is destroyed here automatically
    return &a;
}
// End of block: The outer 'a' is destroyed here, again.

两次销毁对象具有未定义的行为。你不希望这样。

如果你不手动调用析构函数,外部a只会被销毁一次,这很好。

但在任何一种情况下,RecievedMessage a(sc, type, vals);都与外部a无关,只会创建另一个变量。

有很多方法可以解决这个问题,但是代码的最后几行使它毫无意义:

    return &a;

您将返回本地变量的地址。这本身就是破坏的:当函数返回时,它的所有局部变量都会被自动销毁,所以你返回的是一个无效的指针。

答案 1 :(得分:2)

你的代码到处都是,但我认为你正在寻找的是这样的:

ReceivedMessage *MakeReceivedMessage (foo sc, bar type, vector<whatever>& vals)
{
    if (vals.empty())
        return new ReceivedMessage (sc, type);

    return new ReceivedMessage (sc, type, vals);
}

当然,在这个例子中最好只有一个构造函数并让对象测试vals在适当的时候是否为空,但是,一般来说,你可以随时调用你喜欢的任何构造函数。只需正确管理对象的生命周期(并且不要 - 永远 - 返回指向堆栈中对象的指针)。

示例用法(用于管理正确返回的对象的生命周期):

std::unique_ptr<ReceivedMessage> MyReceivedMessage (MakeReceivedMessage (...));
MyReceivedMessage->DoFunkyStuffWithMessage ();
....

或者,正如melpomene指出的那样,您可以首先返回std::unique_ptr<ReceivedMessage>。有些人(很多?)会更喜欢这样。您可以使用std::make_unique构建它。

答案 2 :(得分:1)

目前您的代码存在三个主要问题:

首先,您对析构函数PdfStamper的注释调用不应该存在。在C ++中,当对象的生命周期结束时(或者当它超出范围时,或者当~ReceivedMessage()被动态分配delete时调用new)时,会自动调用对象的析构函数。虽然在某些情况下显式调用析构函数是必要的(例如“放置新的”),但这些情况是您不太可能遇到的情况。

其次,内部RecievedMessage a(sc, type, vals);中的if声明不会替换外部作用域中a的值。这只会创建另一个同名的变量,该变量会遮蔽外部a,而外部作用域中的return &a;只能引用外部a。此时内部a已不再存在,因为它已超出范围。

解决此问题的方法是使用a运算符并构建临时=来为ReceivedMessage分配新值:

if (!vals.empty()) {
    a = ReceivedMessage(sc, type, vals);
}

只要为operator=定义(隐式或其他)正确的ReceivedMessage,这应该有效。

第三,你的函数返回一个指向局部变量a的指针。由于C ++中的对象一旦超出范围就被销毁,到函数返回时a不再存在,因此调用代码获得的ReceivedMessage *指针无效且未定义取消引用该指针并使用它的行为。

此问题有几个问题:

第一个选项不是返回指针(ReceivedMessage *),而是按值返回ReceivedMessage

ReceivedMessage foo()
{
    ReceivedMessage a(123);

    return a;
}

只要为ReceivedMessage定义(隐式或其他)正确的副本或移动构造函数,这应该有效。

第二个选项是使用std::unique_ptr,然后让您的函数返回std::unique_ptr<ReceivedMessage>

#include <memory>

std::unique_ptr<ReceivedMessage> foo()
{
    std::unique_ptr<ReceivedMessage> a;

    if (vals.empty()) {
        a = std::make_unique<ReceivedMessage>(sc, type);
    } else {
        a = std::make_unique<ReceivedMessage>(sc, type, vals);
    }

    return a;
}

此方法的优点是unique_ptr可以为空,因此您可以创建空unique_ptr,而无需立即构建ReceivedMessage。此外,您可以安全地移动和分配unique_ptr值,而无需定义正确的operator=或正确的复制/移动构造函数。

使用unique_ptr时,调用代码可能如下所示:

std::unique_ptr<ReceivedMessage> message = foo();
foo->bar();

而不是直接使用ReceivedMessage时的以下内容:

ReceivedMessage message = foo();
foo.bar();