c++ - 我可以在 C++ 工厂中声明的同一行中使用变量吗?

标签 c++ factory

我有一个多态的类层次结构。虽然我也支持标准工厂方法,我只使用基类指针,但我也想要一个工厂机制给我派生类,这并不容易,因为这些函数仅在返回类型上有所不同。这就是为什么我想出重载函数并让编译器选择正确的想法的原因。

一个简单的应用是,当不再需要类型信息时,我可以编写创建派生对象的函数,“准备”它并返回指向它的基指针以供进一步访问。

  • 问题 1:以下是否正确?
  • 问题 2:newObject() 的参数仅供编译器选择正确的函数。我担心在声明它的同一行上使用 p1 。在 newObject() 中设置它之前,我从未读取过它的值,但我不确定它是否会导致未定义的行为。另外我有一个 self 赋值,因为返回值是赋给自己的...

这是代码示例:

class Base
{
  public:
    virtual ~Base(void){}
};

class Derived1 : public Base
{
  public:
    virtual ~Derived1(void){}
};

class Derived2 : public Base
{
  public:
    virtual ~Derived2(void){}
};

// factory

Base * newObject(int i)
{
    if (i == 1)
    {
        return new Derived1();
    }
    else
    {
        return new Derived2();
    }
}

// family of functions to create all derived classes of Base

Derived1 * newObject(Derived1 *& p)
{
    p = new Derived1();
    return p;
}

Derived2 * newObject(Derived2 *& p)
{
    p = new Derived2();
    return p;
}

int main()
{
    // compiler picks the right newObject function according to the parameter type
    Derived1 * p1 = newObject(p1);
    Derived2 * p2 = newObject(p2);

    // This is safe, right? But it does not convey that it creates something. Hence I prefer the above syntax.
    Derived2 * p3 = nullptr;
    newObject(p3);

    delete p3;
    delete p2;
    delete p1;
}

编辑:

为了避免使用刚刚创建的变量的问题,这是一个替代方案:

Derived1 * newObject(Derived1 *)
{
    // anonymous variable is not used at all, just to pick the right function
    Derived1 * p = new Derived1();
    return p;
}

最佳答案

这是一个有问题的设计,但从纯语言的角度来看是正确的。 C++ 标准是这样说的:

[basic.life]

1 The lifetime of an object or reference is a runtime property of the object or reference. An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. The lifetime of an object of type T begins when:

  • storage with the proper alignment and size for type T is obtained, and
  • if the object has non-vacuous initialization, its initialization is complete,

...

7 before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways... such a glvalue refers to allocated storage, and using the properties of the glvalue that do not depend on its value is well-defined. The program has undefined behavior if:

  • the glvalue is used to access the object, or
  • ...

第7段明确规定,使用这样的引用赋值给一个生命周期还没有开始的对象是UB。

但是根据第 1 段,指针的生命周期从为它分配存储空间开始,因为它是一种具有空初始化的类型。所以从本质上讲,您的代码不会受到第 7 段的威胁。


如果调用newObject时不想指定模板参数(如果你使用 C++11 auto p = newObject<Dervied1>() ,这不是那么糟糕或冗长),并且你至少可以访问 C++11,你可以使用一个通用的模板相关技巧来推迟构造直到类型众所周知。它的实质:

namespace detail {
// ...
template<typename... Args>
struct DefferedConstruct {
    std::tuple<Args&...> args_tuple;

    template<typename T>
    operator T*() {
        return new T(make_from_tuple<T>(
            args_tuple
        ));
    }
};
} // namespace detail

template<typename... Args>
auto newObject(Args&&... args) -> detail::DefferedConstruct<Args...> {
    return detail::DefferedConstruct<Args...>{
        std::forward_as_tuple(args...)
    };
}

class Base
{
  public:
    virtual ~Base(void){}
};

class Derived1 : public Base
{
  public:
    Derived1(int) {}
    virtual ~Derived1(void){}
};

class Derived2 : public Base
{
  public:
    virtual ~Derived2(void){}
};

int main()
{
// compiler construct the right object according to the return type
    Derived1 *p1 = newObject(1);
    Derived2 *p2 = newObject();

    delete p1;
    delete p2;
}

Live Example

上面的代码只是使用了大量的 C++ 魔术来拖拽构造函数参数,直到在函数退出时知道要构造的类。

关于c++ - 我可以在 C++ 工厂中声明的同一行中使用变量吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43887931/

相关文章:

Java:类在包中找不到彼此(工厂设计模式)

c++ - 构造函数中变量的初始化不起作用,而是给我一个随机数

c++ - C++中的指针与变速

c++ - 没有用户输入的gdb回溯?

c++ - 常量指针数组

objective-c - 合成 'global' 对象时出现 EXC_BAD_ACCESS

c++ - 程序跳过一行代码

php - ZF3 多数据库适配器

c# - 数据库失败 db = DatabaseFactory.CreateDatabase();

c++ - 通过 operator new 定义简单对象工厂的正确语法