在C++的构造函数中抛出异常时,销毁对象的成员变量

use*_*738 0 c++ constructor destructor memory-leaks exception

这个问题是基于Scott Meyers在他的"更有效的C++"一书中提供的一个例子.考虑以下课程:

// A class to represent the profile of a user in a dating site for animal lovers.
class AnimalLoverProfile
{
public:
    AnimalLoverProfile(const string& name,
                       const string& profilePictureFileName = "",
                       const string& pictureOfPetFileName = "");
    ~AnimalLoverProfile();

private:
    string theName;
    Image * profilePicture;
    Image * pictureOfPet;
};

AnimalLoverProfile::AnimalLoverProfile(const string& name,
                                       const string& profilePictureFileName,
                                       const string& pictureOfPetFileName)
 : theName(name)
{
    if (profilePictureFileName != "")
    {
        profilePicture = new Image(profilePictureFileName);
    }

    if (pictureOfPetFileName != "")
    {
        pictureOfPet = new Image(pictureOfPetFileName); // Consider exception here!
    }
}

AnimalLoverProfile::~AnimalLoverProfile()
{
    delete profilePicture;
    delete pictureOfPet;
}
Run Code Online (Sandbox Code Playgroud)

在他的书中,Scott解释说如果在对象的构造函数中抛出异常,那么该对象的析构函数将永远不会被调用,因为C++不能销毁部分构造的对象.在上面的示例中,如果调用new Image(pictureOfPetFileName)抛出异常,则永远不会调用类的析构函数,这会导致已分配的内容profilePicture泄露.

他描述了处理这个问题的许多不同方法,但我感兴趣的是成员变量theName.如果new Image构造函数中的任何一个调用引发了异常,那么这个成员变量是否会被泄露?斯科特说它不会被泄露,因为它是一个非指针数据成员,但如果析构函数AnimalLoverProfile从未被调用,那么谁会破坏theName

use*_*670 6

析构函数AnimalLoverProfile从未被调用,因为此对象尚未构造,而析构函数theName将被调用,因为此对象已正确构造(即使它是尚未完全构造的对象的字段).通过使用智能指针可以避免任何内存泄漏:

::std::unique_ptr<Image> profilePicture;
::std::unique_ptr<Image> pictureOfPet;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,new Image(pictureOfPetFileName)抛出时,profilePicture对象将已经被构造,这意味着将调用它的析构函数,就像调用析构函数一样theName.