我有一堆具有一个共同功能的类,除了它返回一个指向它们自己类型的指针。代码看起来一样,我想把它移到一个抽象的基类中。但是,如何使从中继承的类返回自己的类型?
class base {
base *foo() {
// ...
}
};
class derived : public base {
};
derived d;
d.foo(); // Should return derived* instead of base*
有没有办法在C ++中表达这个?
答案 0 :(得分:10)
是的,C ++支持这一点。它被称为协变返回类型。您只需要声明函数virtual并相应地声明返回类型。这就是它的全部内容。
struct base {
virtual base *foo() {
// ...
}
};
struct derived : public base {
virtual derived *foo() {
// ...
}
};
derived d;
base *base_ptr = d.foo();
现在你的评论扩展了原来的问题:
但实际上我的目标是不重复功能体,因为它是 除了使用的类型之外。
这是不可能的。
有各种各样的技巧可以促进重复,但是你不知道无论你做什么,你仍然需要自己创建功能体。
其中一种技术就是使用宏,代价是混淆以及宏带来的所有其他缺点;但是,宏仍然不会自动出现在课程中。你必须把它放在那里。
// beware of macros!
#define FOO(T) virtual T *foo() { return new T; }
struct base {
FOO(base)
virtual ~base() {} // let's not forget the virtual destructor
};
struct derived : public base {
FOO(derived)
};
类似的方法是使用模板来促进函数体的重复:
template <class T>
T *ComplicatedFunctionReturningT()
{
T *t;
// ...
// ...
// ...
return t;
}
struct base {
virtual base *foo() {
return ComplicatedFunctionReturningT<base>();
}
virtual ~base() {} // let's not forget the virtual destructor
};
struct derived : public base {
virtual derived *foo() {
return ComplicatedFunctionReturningT<derived>();
}
};
模板比宏更安全。
另一种方法是使用模板方法设计模式。如果在每个类的函数体中重复大量代码,请尝试在基类中尽可能多地移动并将小的抽象部分放入要覆盖的私有函数中:
class base {
public:
base *foo() { // no longer virtual
// ...
// ...
base *ptr = fooImpl();
// ...
// ...
return ptr;
}
virtual ~base() {} // let's not forget the virtual destructor
private:
virtual base *fooImpl() = 0; // pure virtual and private
};
class derived1 : public base {
private:
virtual derived1 *fooImpl() {
return new derived1; // very simple body
}
};
class derived2 : public base {
private:
virtual derived2 *fooImpl() {
return new derived2; // very simple body
}
};
当然,如果函数体非常复杂,那么所有这些都是值得的。对于极端情况,完全不同的方法是使用一些外部工具或脚本生成C ++代码。
最后,如果这确实是个问题,请重新考虑整个设计。也许事实证明你并不真正需要这个功能,或者你不需要OOP来解决你的程序试图解决的实际问题。
答案 1 :(得分:3)
根据你对基督徒回答的评论,你可以实现一个模板助手方法,不要重复你将要使用的代码:
class base
{
protected:
template<class T> T* fooInternal()
{
T* t = new T();
// do stuff with t
return t;
}
public:
virtual base* foo() { return fooInternal<base>(); }
};
class derived : public base
{
public:
virtual derived* foo() { return fooInternal<derived>(); }
};
答案 2 :(得分:2)
一种选择是使用CRTP(奇怪地重复使用模板模式)。
template<typename Derived>
struct base {
Derived* foo() {
// ...
return static_cast<Derived*>(this);
}
};
struct derived : base<derived> {
// ...
};
derived d;
derived *derived_ptr = d.foo();
主要优点是您不需要宏或虚拟方法即可获得所需的类型。
如果要在基类中使用多个方法来执行此操作,最好定义一个辅助方法。
template<typename Derived>
struct base {
Derived* foo() {
// ...
return self();
}
private:
Derived* self() {
return static_cast<Derived*>(this);
}
};
例如,我已将这种模式用于属性对象,这些属性对象具有一组共享属性和一些具有其自身属性的派生属性对象。然后,我让基类方法返回对派生类的引用,以便可以在同一条语句上链设置多个属性。
SomePropertyObject()
.setBaseProperty("foo")
.setDerivedProperty("bar");