我有一个多态的类层次结构。虽然我也支持标准工厂方法,即仅使用基类指针,但我还想要一个为我提供派生类的工厂机制,这并不容易,因为这些函数仅在返回类型上有所不同。这就是为什么我想到重载函数并让编译器选择正确的函数的原因。
一个简单的应用是,我可以编写函数来创建派生对象,“准备”它并返回指向它的基指针,以便在不再需要类型信息时进行进一步访问。
- 问题1:下面这个可以吗?
- 问题2:newObject()的参数只是为了让编译器选择正确的函数。我担心使用
p1
在声明的同一行。在设置它之前我从未读取过它的值newObject()
,但我不确定它是否会导致未定义的行为。我还有一个自我分配,因为返回值被分配给自己......
这是代码示例:
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;
}
EDIT:
为了避免使用刚刚创建的变量的问题,这是一种替代方法:
Derived1 * newObject(Derived1 *)
{
// anonymous variable is not used at all, just to pick the right function
Derived1 * p = new Derived1();
return p;
}
这是一个有问题的设计,但从纯粹语言的角度来看是正确的。 C++ 标准是这么说的:
[基本生活] http://eel.is/c++draft/basic.life
1 对象或引用的生命周期是其运行时属性
对象或参考。一个物体被称为非空的
初始化(如果它是类或聚合类型)并且它或其中之一
它的子对象是由一个构造函数而不是一个简单的构造函数初始化的
默认构造函数。 T 类型对象的生命周期从以下时间开始:
- 获得类型 T 具有适当对齐和大小的存储,并且
- 如果对象具有非空初始化,则其初始化已完成,
...
7 在对象的生命周期开始之前但在存储之后
对象将占用的空间已分配或在生命周期结束后
对象的存储已经结束并且在该对象的存储之前
占用被重用或释放,任何引用的左值
可以使用原始对象,但只能以有限的方式使用......例如glvalue
指分配的存储空间,并使用泛左值的属性
不依赖于它的值是明确定义的。该计划有
未定义的行为,如果:
第 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 http://ideone.com/ilP0gq
上面的代码只是使用了大量的 C++ 魔法来拖拽构造函数参数,直到在函数退出时知道要构造的类。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)