我对CRTP有一些疑问。假设我有以下代码
#include <iostream>
// interface
template<class Imp>
class Interface{
public:
inline void print(void){
std::cout<<"value: ";
return asImp().print();
}
private:
typedef Imp Implementation;
inline Implementation& asImp(void){return static_cast<Implementation&>(*this);}
};
// add
class Add:public Interface<Add>{
public:
inline void print(void){std::cout<<value<<std::endl;++value;}
private:
int value;
};
// main
int main(void){
Interface<Add> foo;
foo.print();
foo.print();
}
输出
value: 0
value: 1
因此,变量value
似乎由默认构造函数构造为0。但我不明白这个构造函数的调用位置和时间,因为没有创建派生类的对象。
此外,假设我想要创建具有不同起始值的值,我如何使用此设计模式实现该目标?
显然,我可以在派生类中创建一个init()
方法,该方法在基类的构造函数中调用,但它不适用于没有默认构造函数的类型。
最后,是否可以将一些参数传递给基类的构造函数转发给派生类的构造函数?
答案 0 :(得分:2)
您实际上不创建派生对象。
您正在创建基类对象并将其转换为派生类型的引用,但基础对象仍然是基类,因此它的错误。
static_cast
总是成功,但是如果你没有强制转换为正确的类型会引发未定义的行为,而dynamic_cast
如果你输入错误则会返回NULL指针。
- &GT;在这里试一试,你可能会或可能不会得到垃圾值:http://ideone.com/dP3jjU
一旦你解决了上述问题,另一个初始化问题应该是直截了当的。
更多相关信息:Should static_cast<Derived *>(Base pointer) give compile time error?
答案 1 :(得分:1)
在源代码中,您自己将基类命名为Interface
。在面向对象的意义上,您不创建Interface
的实例,而是创建源自Interface
的类的实例。您在main中的代码错误地实例化Interface
而不是从它派生的类。您可以通过强制Interface
使用无法实例化的属性来解决此问题。 E.g:
template<class Imp>
class Interface {
//...
protected:
Interface () {} // not accessible except by derived
};
与常规多态性不同,您实际上不希望传递Interface
个对象。 Interface
通过提供预期的方法来提供特定接口的强制执行,而从Interface
派生的方法必须采用该接口的期望。您的示例有点人为,因为Interface
实际上只是派生到派生中的相同命名方法。但是,更好的示例是Interface
使用它期望派生类型提供的属性提供实现的示例。
在下面的示例中,我们看到Worker
继承了WorkerInterface
的界面。但是,由于WorkerInterface
的期望,需要实施perform()
和wait()
。虽然纯接口使用纯虚方法强制执行此要求,但CRTP通过模板扩展强制执行此操作。
template <typename JOB>
class WorkerInterface {
public:
void work () { while (job().wait()) job().perform(); }
private:
JOB & job () { return *static_cast<JOB *>(this); }
protected:
WorkerInterface () {}
};
class Worker : public WorkerInterface<Worker>
{
friend class WorkerInterface<Worker>;
int state_;
void perform () { std::cout << "Worker: " << __func__ << '\n'; }
bool wait () { return state_--; }
public:
Worker () : state_(1) {}
};
int main ()
{
Worker w;
w.work();
}
现在,只要派生类提供WorkerInterface
和{的合适实现,任何派生自work()
的类都将提供wait()
方法,该方法将执行“正确”的操作。 {1}}。