C++避免构造对象

发布于 2024-11-03 08:55:20 字数 787 浏览 0 评论 0原文

我有一个类,其所有默认构造函数都超出了我的控制范围。我需要避免在某些情况下构建它们,而在其他情况下则不然。有什么我可以做的吗?

模式是这样的:

class A
{
public: 
    //ctor + dtor + methods
    A(); 
    ~A();
    // Data members beyond my control
    X1 x1;
    X2 x2;
    ...
    XN xn;
};

class B
{
public:
    B()
    {}

    // Data members
    A a;   // This instance of A is the issue
};

问题是 X1XN 完全超出了我的控制范围。 A 类只是将 x1xn 打包在一起的类。我想有条件地在 B 中创建 A 的实例。现在的问题是,X1XN 的默认构造函数会执行某些在某些情况下需要避免的操作,而在其他情况下则不需要。因此,无论我是否构造 AB 都会尝试使用 A 的默认构造函数创建一个,这反过来又会启动默认值X1XN 的构造函数。这是我需要避免的。

我目前正在使用宏来解决这个问题,但想看看是否有更好的解决方案。

I have an class whose all default constructors are beyond my control. I need to avoid constructing them in some conditions and not in others. Is there any what I can do this ?

The pattern is something like this:

class A
{
public: 
    //ctor + dtor + methods
    A(); 
    ~A();
    // Data members beyond my control
    X1 x1;
    X2 x2;
    ...
    XN xn;
};

class B
{
public:
    B()
    {}

    // Data members
    A a;   // This instance of A is the issue
};

The issue is X1 to XN are completely beyond my control. Class A is just a class which packages x1 to xn together. I want to conditionally create the instance of A, in B. Now the issue is that the default constructors of X1 to XN do something that needs to be avoid under some conditions and not others. So no matter if I construct A or not, B will try to create a using the default constructor of A, which will in turn initiate default constructors of X1 to XN. This is what I need to avoid.

I am using macros to fix this currently, but wanted to see if there is a better solution out there.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

无人问我粥可暖 2024-11-10 08:55:20

正如康拉德·鲁道夫指出的:

class B
{
public:
  B(bool create = false)
  {
    a = (create ? new A : NULL);
  }
  ~B()
  {
    delete a;
  }
private:
  A* a;
};

As Konrad Rudolph pointed out:

class B
{
public:
  B(bool create = false)
  {
    a = (create ? new A : NULL);
  }
  ~B()
  {
    delete a;
  }
private:
  A* a;
};
疯了 2024-11-10 08:55:20

您可能想使用 Nullable 类型,例如 boost::Optional。这看起来像:

class B {
public:
    B()
    {
        /*
         * Here, a has been default constructed and is empty
         * You can do computations here and then...
         */
        if(/* some elaborate condition*/) {
            a = A();
            /* access *a from here on */
        } else {
            /* anything you want */
        }
    }

private:
    boost::optional<A> a;
};

这回答了问题,但我认为如果你告诉你真正想要实现的目标,可以给出更合适的答案。我觉得这更多是一个设计问题而不是语言问题。接下来是扩展的想法。

在上面的“解决方案”中,else 子句中包含什么内容?由于 A 显然只能默认构造,因此您不能调用不同的构造函数。但是,如果您不初始化 a,则会引入(圈)复杂性,因为这意味着每个方法都必须检查 a 是否处于活动状态。或者你可以抛出;然而,我会重构在函数中进行检查(或其他)的代码;私有静态方法或匿名/静态独立函数,如下所示:

namespace {
A
prepare_A()
{
    /* elaborate computations, possibly throw */
    return A();
    /*
     * if A had different constructors we could also conditionally
     * return an A(0) or whatever.
     */
}
} // namespace

B::B()
:
    a(prepare_A())
{
    /*
     * a is of type A, not boost::optional<A>
     */
}

但是,这假设 A 是可复制的。如果情况并非如此,或者复制 A 是不可接受的,我认为第一个解决方案是可以接受的,条件是作为类不变 a 的一部分永远不为空。

如果我们知道 AB 之间的关系,那么推荐一些东西就会更容易。如果您想有条件地初始化 A 成员,为什么要将它放入 B 中呢?也许两者之间的首选关系不应该是聚合而是关联:指向 A 的指针成员(或引用成员,如果您小心赋值运算符),例如:

class B {
public:
    explicit
    B(A& a_)
    /*
     * Important: not A const&,
     * we only want lvalues.
     * Contract on the caller: a must remain valid during
     * the lifetime of *this.
     */
    :
        a(&a_)
    {
        /*
         * class invariants: a != 0,
         * *a remains valid for the lifetime of B
         */
    }

private:
    A* a;
};

这样就不会引入圈复杂度你并不关心 A 是如何以及何时构建的。该契约给调用者增加了一些负担,但由于我们只能将左值传递给构造函数,因此很难滥用。

You may want to use a Nullable type like boost::optional. This would look like:

class B {
public:
    B()
    {
        /*
         * Here, a has been default constructed and is empty
         * You can do computations here and then...
         */
        if(/* some elaborate condition*/) {
            a = A();
            /* access *a from here on */
        } else {
            /* anything you want */
        }
    }

private:
    boost::optional<A> a;
};

This answers the question but I think a more appropriate answer could be given if you told what you really wanted to achieve. I feel this is more of a design issue rather than a language issue. Extended thoughts follow.

In the above 'solution', what goes in the else clause? Since A can apparently only be default-constructed, it's not like you can put a different constructor call. If you do not however initialize a you introduce (cyclomatic) complexity since that would mean that every method will have to check if a is active. Alternatively you could throw; I would however refactor the code that do the checking (or whatever) in a function; either a private static method or an anonymous/static freestanding function like this:

namespace {
A
prepare_A()
{
    /* elaborate computations, possibly throw */
    return A();
    /*
     * if A had different constructors we could also conditionally
     * return an A(0) or whatever.
     */
}
} // namespace

B::B()
:
    a(prepare_A())
{
    /*
     * a is of type A, not boost::optional<A>
     */
}

This however assumes that A is copyable. If that's not the case or it's not acceptable to copy A around, I'd consider the first solution acceptable on the condition that as part of the class invariant a is never empty.

It'd be easier to recommend something if we knew the relationship between A and B. Why would you put an A member in B if you want to conditionally initialize it? Perhaps the preferred relationship between the two should not be aggregation but association: a pointer member (or reference member if you're careful about the assignment operator) to A such as:

class B {
public:
    explicit
    B(A& a_)
    /*
     * Important: not A const&,
     * we only want lvalues.
     * Contract on the caller: a must remain valid during
     * the lifetime of *this.
     */
    :
        a(&a_)
    {
        /*
         * class invariants: a != 0,
         * *a remains valid for the lifetime of B
         */
    }

private:
    A* a;
};

That way you introduce no cyclomatic complexity and you don't care how and when an A is constructed. The contract adds a bit of a burden on the caller but since we can only pass lvalues to the constructor it's hard(er) to misuse.

嘴硬脾气大 2024-11-10 08:55:20

目前还不清楚您想要避免哪种构造函数,但在任何一种情况下您都可以使用联合。

来自 Stroustrup 第 4 版:

如果联合体的成员具有用户定义的构造函数、复制操作、移动操作或析构函数,则该联合体的特殊函数将被删除(第 3.3.4 节、第 17.6.4 节);即不能用于union类型的对象。

因此,如果您不想在 B 中构造 A 时,请使用:

class B {
public:
    B()
    {}

    // Data members
    union {
       A a;   // This instance of A is the issue
    };
};

It's unclear which constructor you want to avoid but in either case you can use a union.

From Stroustrup 4th ed:

If a union has a member with a user-defined constructor, a copy operation, a move operation, or a destructor, then that special function is deleted (§3.3.4, §17.6.4) for that union; that is, it cannot be used for an object of the union type.

So if you want to not construct A when it is in B, use:

class B {
public:
    B()
    {}

    // Data members
    union {
       A a;   // This instance of A is the issue
    };
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文