【问题标题】:Initializing an object inside the constructor and not in initialization list在构造函数中而不是在初始化列表中初始化对象
【发布时间】:2013-01-12 18:18:41
【问题描述】:

我有以下包含 3 种数据类型的类:

   class CentralBank{
    MaxHeap richestBanks;
    HashTable banks;
    AccountTree accounts;

public:
    CentralBank(int numAccounts, Account* accounts, int numBanks, Bank* bankArr);
    void AddAccount(Account account);
    void RemoveAccount(int accountID);
    void AddBank(Bank bank);
    int GetAccountsNumber(int bankID);
    void GetKRichestBanks(unsigned int K, Bank* banks);
    int GetSumBalance (int low, int high);

};

这是构造函数:

CentralBank::CentralBank(int numAccounts, Account* accounts, int numBanks,
        Bank* bankArr): accounts(numAccounts,accounts){
    int** locs = new int*[numBanks];
    richestBanks = MaxHeap(numBanks,bankArr, locs);
    banks = HashTable(numBanks,bankArr,locs);
    delete[] locs;
}

我的问题是堆和哈希表的析构函数是在它们的构造函数之后调用的。如果我把它们都变成指针,它就不会发生。 为什么会这样? 有没有办法让它们不是指针,也没有在初始化后立即调用析构函数?我没有正确初始化它们吗?

PS:它们不在初始化列表中,因为它们的构造函数需要需要初始化的“locs”参数。

【问题讨论】:

  • 你看到的析构函数是临时的。
  • "它们不在初始化列表中,因为它们的构造函数需要需要初始化的“locs”参数。" 然后修复那个 .这就是导致您不得不使用不稳定的初始化技术的原因。
  • @NicolBolas:我可以,但我不愿意。不过,我想了解为什么这不起作用,所以我不会犯同样的错误。
  • @Shookie :您的错误在于编写代码时首先使用了new。 ;-]
  • 或者,如果可以的话,一个 C++11 智能指针或 boost 智能指针。

标签: c++ constructor initialization destructor


【解决方案1】:

一旦进入构造函数的主体,C++ 的规则保证该类的所有基类和该类的所有成员都已被初始化。这就是为什么初始化列表在构造函数体之外的原因;因为它在您的构造函数主体之前被调用。如果你没有在你的构造函数的初始化列表中指定构造函数和参数,那么它将被默认初始化。

所以richestBanksbanks此时已经被初始化了。而且你不能两次初始化一个对象。

richestBanks = MaxHeap(numBanks,bankArr, locs);

这样做是创建一个 new MaxHeap 临时对象,然后调用复制赋值运算符(或移动赋值,在适当的情况下)将新数据复制到 richestBanks。之后,必须销毁临时对象。这就是您看到的析构函数调用。

正确的解决方案是停止做任何你需要locs做的事情,并找到更好的方法来构造你的数据,以便你可以正确使用初始化列表。

【讨论】:

  • 感谢您的精彩回答!那么我没有办法“重新初始化”它们吗?不管怎样,我可能会按照你的建议做,并用 locs 删除整个东西
  • @Shookie 如果您将它们声明为 shared_ptrs 并在构造函数中创建它们,那么您应该没问题。
【解决方案2】:
richestBanks = MaxHeap(numBanks,bankArr, locs)

我记得,这意味着你创建一个临时对象,运行复制构造函数将它复制到 richestBanks 变量,然后销毁这个临时变量。

更好的解决方案是引用对象 (MaxHeap&) 而不是对象本身或指针。

【讨论】:

  • 差不多,是拷贝赋值操作符,不是拷贝构造函数。
  • 即使你把它们放在初始化列表中,也可能调用析构函数。
【解决方案3】:

我的问题是堆和哈希表的析构函数是在它们的构造函数之后调用的。

正在为 CentralBank 构造函数主体中构造的 MaxHeap 和 HashTable 的临时实例调用析构函数,这些实例最终超出了范围。这些临时变量复制到成员变量richestBanksbanks 中,它们在进入此构造函数的主体时已经初始化

假设您确实需要 int 指针数组,您可以根据正常的 RAII 指南设置一个辅助类来处理其生命周期。像这样的:

class IntPointerArray
{
public:
    IntPointerArray( int num )
    : array_(new int*[num])
    {}
    ~IntPointerArray()
    { delete [] array_; }
    operator int** ()
    { return array_; }
private:
    int** array_;
};

现在,扩展您的类以将 this 的实例作为成员:

   class CentralBank{
    IntPointerArray locs;
    MaxHeap richestBanks;
    HashTable banks;
    AccountTree accounts;
    // rest omitted

遵守成员按照声明顺序初始化的规则,您的构造函数现在可以如下所示:

CentralBank::CentralBank(int numAccounts, Account* accounts, int numBanks, Bank* bankArr)
: locs(numBanks)
, richestBanks(numBanks,bankArr, locs) // exploits operator int**
, banks(numBanks,bankArr, locs) // ditto
, accounts(numAccounts,accounts)
{}

这样一来,所有成员都直接初始化了,根本不需要带临时的构造函数体。

您是否应该使用这样的外部数组也值得研究。我猜你真的需要被称为treap 的数据结构。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-06
    • 1970-01-01
    • 2012-10-23
    相关资源
    最近更新 更多