您的位置:首页 > 编程语言 > C语言/C++

关于c++ template多态——CRTP 模式

2017-06-28 20:08 337 查看
看了很多有关CRTP(Curiously Recurring Template Prattern)的介绍,知道CRTP是什么,但不知道究竟应该在什么情况下用,请高手回答。

为了便于说明,以下给出三种类的继承方式:

第一种,普通继承,没有虚函数,子类同名函数完全覆盖父类:

struct Base

{

void Func() { std::cout << " base's func" << std::endl; }
};

struct Drv : public Base

{

void Func() { std::cout << " drv's func" << std::endl; }
};

第二种,普通继承,用虚函数:

struct Base

{

virtual void Func() { std::cout << " base's func" << std::endl; }
};

struct Drv : public Base

{

void Func() { std::cout << " drv's func" << std::endl; }
};

第三种:采用CRTP模式:

template <typename Derived>

struct Base

{

void Func() { static_cast<Derived&>(*this).FuncImpl(); }
void FuncImpl() { std::cout << "base's func" << std::endl; }
};

struct Drv : public Base<Drv>

{

void FuncImpl() { std::cout << "drv's func" << std::endl; }
};

我想问的是,为什么需要第三种方式???在我看来第三种和第一种比并没有多大优势。

第一种方式实际仅仅实现了子类对父类的共享,即公共的操作写在父类,子类仅仅实现个性的细节。

第二种方式除实现共享外,可以用父类的指针或引用实现多态,即:

Base* p = new Drv; p->Func();

甚至可以将父类的指针搜集到容器,之后可以进行无区别地批量处理(假设Drv1, Drv2, Drv3...均继承Base):

/// 搜集

std::vector<Base* p> container;

container.push_back(new Drv1);

container.push_back(new Drv2);

container.push_back(new Drv3);

...

/// 批量处理

for (unsigned int i = 0; i < container.size(); ++i) {

container[i]->Func();
}

第三种方式没有虚函数,的确可以省去动态绑定所需的开销,而且能够用于虚函数无法应用的地方,如内联,或函数模板。但它与第一种方式的区别何在?

虽然也可以用父类指针实现多态:Base<Drv>* p = new Drv; p->Func();

但由于父类必须显式指定子类作为模板参数,从应用的角度说它的优势体现在哪?这和如下写法有何区别: Drv* p = new Drv; p->Func(); (用第一种方式就可以实现)

另外,采用CRTP能将父类指针存放于容器中,然后进行无区别地处理吗(如第二种方式一样)??

如果以上两点都做不到,我不知道为什么还要采用CRTP,而不直接用第一种方式??它的应用点到底在哪???或者能做到,如何做?

我模板只是初步了解,还请高手指教,非常感谢!!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: