模板化类中的析构函数实现

时间:2016-11-30 09:11:41

标签: c++ class templates destructor delete-operator

如果我有这样的课程:

Array<int> miArray(5);

如果现在我像这样创建我班级的对象:

Array<int*> miArray(5);

析构函数的实现应该没问题,但是如果我创建这样的对象:

SELECT clients.*, salesagents.name, COUNT(DISTINCT v1.id) as visits_number, COUNT( DISTINCT v2.id) as visits_number_last_month
FROM `clients`
LEFT JOIN `salesagents` ON `clients`.`salesagents_id`=`salesagents`.`id`
LEFT JOIN `visits` as `v1` ON `clients`.`id` = `v1`.`clients_id`
LEFT JOIN `visits` as `v2` ON `clients`.`id` = `v2`.`clients_id` AND `v2`.`date` > FROM_UNIXTIME(UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 1 MONTH)))
GROUP BY `clients`.`id`

在析构函数中,我应该删除存储在数组中的每个对象,以避免内存泄漏。

我怎样才能做到这一点?

由于

2 个答案:

答案 0 :(得分:4)

您可以使用a specialization of your destructor或tag-dispatch来删除功能(这会更灵活)。然而,这导致了一种非常笨拙和脆弱的设计。假设您已实现等效于push_back的内容,请考虑以下用户代码段:

{
    Array<int*> arr;
    arr.push_back(new int(42)); // new is on the side of the user
} // ... but delete is not. Weird.

这也会导致一系列错误:你只能将new编辑T添加到Array<T*>,但是没有任何安全措施可以传递其他内容:< / p>

{
    Array<int*> arr;
    int i = 42;
    arr.push_back(&i); // No diagnostic
    arr.push_back(new int[17]); // No diagnostic either
} // Undefined behaviour from calling `delete` on stuff that hasn't been `new`ed

所有这些都是存在 No raw owning pointers 规则的原因:原始指针根本不应该管理资源生命周期。如果我使用Array<int*>,它应该存储我的指针并允许我使用它们,但永远不会永远 delete它们,因为它试图管理生命周期。

相反,如果我想要一个管理生命周期的Array,我将使用相应的智能指针Array<std::unique_ptr<int>>。这与Array很好地联系,要求它只调用所包含对象的析构函数(它已经完成)。这些析构函数(~unique_ptr)将会根据自己的工作过度释放资源,一切都很顺利。

附加说明:

  • 如果需要,请注意初始化Array的缓冲区 - m_pData = new T[m_nSize];将分配默认初始化对象。如果这些对象没有默认构造函数,则它们的值将是不确定的。一个简单的解决方法是使用new T[m_nSize]{},它将执行值初始化 - 即将算术类型初始化为零,指向nullptr的指针和递归的复合类型。

  • 为您的班级实施复制和/或移动语义,请参阅Rule of three/five/zero。就像现在一样,复制Array的实例将导致两个实例认为它们拥有相同的缓冲区,并且当两个实例都尝试delete时都会有未定义的行为。

  • deletedelete检查是否为null,因此析构函数中的if(m_pData != NULL)是多余的。

祝你好运:)

答案 1 :(得分:2)

我认为一个好的设计就是在 smart 指针类中包装原始的拥有指针(例如你的示例中的int*),例如{{1 }或std::unique_ptr,或其他一些RAII包装器。 这就是std::shared_ptr基本上发生的情况,如果你想在向量中存储指向T的拥有指针,你可以拥有std::vectorvector<unique_ptr<T>>

请注意,在容器中存储原始观察指针很好(只要正确处理指向对象的生命周期)。

只需补充几点注意事项:

  1. 您的班级应该禁止通过vector<shared_ptr<T>>复制构造函数和复制分配进行复制,或者正确实现这些复制操作。因为它处于当前状态,如果人们试图复制构造或复制分配您的类的实例,则容易出错。

  2. =delete构造函数应标记为Array(unsigned int nSize),以避免来自无符号整数的隐式转换。