【发布时间】:2019-08-09 12:38:05
【问题描述】:
现在我不确定我是否只是在这里使用了糟糕的设计模式,所以很高兴接受一些关于这方面的意见,但这就是我想要做的。
我有很多按层次结构排列的类,但是基类由于它们的使用方式,不能有虚拟析构函数。 (如果他们能做到这一点会容易得多!)从本质上讲,它们只是数据的聚合。我遇到的问题是,我需要在这些派生类上创建不同的类型和调用方法,还要(之后)调用基类的方法。您可以通过在每个 switch 案例中调用基类方法来解决这个问题,但这看起来很难看,所以我正在寻找一种在 switch 之外调用它们的方法。我在这里遇到的问题是我有指向派生对象的基指针并且不知道它实际上是哪个派生对象,并且由于基没有虚拟析构函数,如果通过基指针删除它会发生内存泄漏。所以这就是我想出的:
class Base
{
public:
Base() { std::cout << "Base constructor called" << std::endl; }
~Base() { std::cout << "Base destructor called" << std::endl; }
void SetTimer(void) {}
void SetStatus(void) {}
void SetLogger(void) {}
protected:
uint32_t data1, data_2;
uint32_t data3;
};
class Derived1 : public Base
{
public:
Derived1() { std::cout << "Derived1 constructor called" << std::endl; }
~Derived1() { std::cout << "Derived1 destructor called" << std::endl; }
void Special1(void) { data1 = data2 = 0; }
};
class Derived2 : public Base
{
public:
~Derived2() { std::cout << "Derived2 destructor called" << std::endl; }
void Special2(void) { data3 = 0; }
};
class Derived3 : public Base
{
public:
~Derived3() { std::cout << "Derived3 destructor called" << std::endl; }
void Special3(void) { data1 = data2 = data3 = 0; }
};
//template<typename T>
//struct deleter
//{
// void operator()(T* p) const { delete p; }
//};
int main(int argc, char** argv)
{
int cl = 1;
{
auto deleter = [](auto* t) { delete static_cast<decltype(t)>(t); };
// std::unique_ptr<Base, deleter<Base*>> d1;
std::unique_ptr<Base, decltype(deleter)> d1(new Base(), deleter);
switch (cl)
{
case 1:
{
d1 = std::unique_ptr<Derived1, decltype(deleter)>(new Derived1(), deleter);
// d1 = std::unique_ptr<Derived1, decltype(deleter<Derived1*>)>(new Derived1(), deleter<Derived1*>());
static_cast<Derived1*>(d1.get())->Special1();
break;
}
case 2:
{
d1 = std::unique_ptr<Derived2, decltype(deleter)>(new Derived2(), deleter);
static_cast<Derived2*>(d1.get())->Special2();
break;
}
case 3:
{
d1 = std::unique_ptr<Derived3, decltype(deleter)>(new Derived3(), deleter);
static_cast<Derived3*>(d1.get())->Special3();
break;
}
}
d1.get()->SetLogger();
d1.get()->SetStatus();
d1.get()->SetTimer();
}
return 0;
}
所以在每个 case 语句中,我都必须创建一个新的对象类型,但需要一些我以后可以用来调用基本方法的东西。正如您在注释掉的代码中看到的那样,我确实考虑过使用仿函数来绕过 unique_ptr 声明,但想不出一个让它工作的好方法。
我最大的问题是这一行:
std::unique_ptr<Base, decltype(deleter)> d1(new Base(), deleter);
创建基类似乎并不好,因为我需要在声明时提供删除器。如您所见,我考虑了仿函数方法,但这似乎没有帮助,因为模板仿函数的声明与 std::unique_ptr 不同。
现在我不知道这是否是代码问题,或者我是否只是选择了一个糟糕的设计模式来做这种事情,或者我被注释掉的代码是否可以工作。无论如何,任何帮助将不胜感激。
编辑编辑
感谢您的帮助,但类层次结构中必须没有虚拟方法或虚拟析构函数,因为创建的对象必须完全符合标准布局。所以 std::is_standard_layout::value 应该返回 true。因此,虽然我很欣赏人们说没有虚拟析构函数是一个奇怪的要求,但这是我必须使用的。 (哦,它是在没有虚函数的情况下具有继承的完全合法的用例。)
【问题讨论】:
-
“但是基类由于它们的使用方式,不能有虚拟析构函数”——为什么不呢?
-
“基类不能有虚拟析构函数”是一个奇怪的要求......你能至少有一个(纯)虚拟方法,如
Destroy或其他东西,可以清理吗?或者,也许您可以重新组织您的课程以遵循零规则? -
如果你的类不具有多态性,你就不能拥有多态性。您可以选择运行时多态(
virtual成员)或编译类型多态(模板和访问者模式)。但有些东西必须给予。你需要选择一个。 -
鉴于您的任意“要求”,即基类不能具有虚拟析构函数,使用
delete“通过基类指针”会给出未定义的行为,除非您的代码专门跟踪每个对象的 ACTUAL 类型,将基指针转换为正确类型的指针,然后将其删除。 -
因为基类没有虚拟析构函数——那么基类就不是为了派生而设计的。如果该类被设计为派生自,那么析构函数将是虚拟的。
标签: c++ smart-pointers