【问题标题】:Prevent Base class constructor being called on invalid Derived class initializer防止在无效的派生类初始化程序上调用基类构造函数
【发布时间】:2020-07-06 23:49:41
【问题描述】:

考虑以下示例,其中Derived 类的构造在其构造函数的初始值设定项列表中采用了一个指针。当然我要检查这个指针是否有效,否则抛出异常。

我的尝试阻止了程序崩溃,但 Base 部分仍然在我可以抛出异常之前构建。

有没有办法防止在这种情况下调用 Base 类构造函数?

#include <stdlib.h>
#include <iostream>

class Base
{
public:
    Base(int val) : val_b(val)
    {
        std::cout << "Base::CTOR" << std::endl;
    }
    ~Base() { }

    int val_b;
};

class Derived : public Base
{
public:
    Derived(int *, int);
    ~Derived() { }

    int val_d;

    void print(void)
    {
        std::cout << "Base:\t" << val_b << std::endl;
        std::cout << "Derived:" << val_d << std::endl;
    }

};

Derived::Derived(int *val1, int val2) : Base(val1 ? *val1 : -1), val_d(val2) 
{
    if (!val1)
    {
        throw std::invalid_argument("bad pointer");
    }
}

int main()
{
    int *a = NULL;  
    int b = 43;

    try 
    {
        Derived *d = new Derived(a, b);
        d->print();
    }
    catch (std::exception &e)
    {
        std::cout << "Exception: " << e.what() << std::endl;
    }

    return 0;
}

【问题讨论】:

  • 这听起来像XY problem。你能解释一下你为什么想要这个吗?
  • 如果它得到无效值(代码中的-1),你就会把它扔进 Base ctor
  • 如果使用空指针调用Derived 构造函数是无效的,为什么将参数类型设为指针?为什么不按值或引用传递,避免整个问题?
  • @SanderDeDycker 实际上我将一个对象指针传递给Derived 类ctor 并且只需要一个成员来进行Base 初始化-我只是通过使用int* 来简化它

标签: c++ constructor derived-class


【解决方案1】:

您可以在调用 Base 构造函数之前调用函数/lambda:

Derived::Derived(int *val1, int val2) :
    Base([&](){
        if (!val1) {
            throw std::invalid_argument("bad pointer");
        }
        return *val1;
    }()),
    val_d(val2) 
{
}

【讨论】:

  • “一切皆有可能”的好例子;)这是否是一个好主意是一个不同的问题(OP 没有问)
  • @Odysseus:错字已修复。
【解决方案2】:

我不明白您为什么想要它,但无论如何您是否尝试过在 Base ctor 而不是 Derived ctor 中失败?

class Base
{
public:
    Base(int *val)
    {
        if (!val)
        {
            throw std::invalid_argument("bad pointer");
        } 
        val_b = val;
        std::cout << "Base::CTOR" << std::endl;
    }
    ~Base() { }

    int val_b;
};

【讨论】:

  • 当然我可以重组我的课程,但我想知道是否有一些优雅的方法可以避免这种情况
【解决方案3】:

也许我误解了你的问题,但请考虑这个简化的例子:

#include <iostream>

struct Base {
    ~Base() { std::cout <<"destructor";}
};
struct Foo : Base {
    Foo() : Base() {
        throw 1;
    }
};
int main()
{
    try {
        Foo f;
    } catch(...){}
}

输出是:

destructor

我的尝试阻止了程序崩溃,但在我抛出异常之前仍然构建了 Base 部分。

这不是问题。与往常一样,堆栈被展开并且FooBase 部分被正确销毁。我认为您的代码没有任何问题(在严重损坏的意义上,尽管设计值得商榷)。如果构造失败并且您在构造函数的主体中抛出异常,则清理已经构造的内容是您能做的最好的事情。

【讨论】:

  • 我并不担心基类部分没有正确释放——我只是想避免在初始化列表中出现无效指针时调用它的构造函数
  • @Odysseus 是的,但不清楚你为什么要这样做。在这种情况下,有两种方法可以解决这个问题:1)对最初的意图进行一些猜测并提出不同的解决方案 2)按字面意思回答问题。你基本上对这两个都有答案,我选择了 1) 并且似乎我的猜测不太正确
  • 好吧,实际的问题是,当派生类初始化器列表中存在无效指针时,是否可以防止调用基类ctor。对此的答案似乎是否定的——到目前为止,唯一的方法似乎是使用 lambda 函数。但是知道了这一点,我现在可以考虑重组我的课程
  • 为什么你说答案是否定的? Jarods 回答显示了如何防止 Base 构造函数被调用。虽然我同意你应该修复设计
猜你喜欢
  • 2011-09-09
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-19
  • 2013-09-16
  • 2018-07-16
相关资源
最近更新 更多