【问题标题】:Different templated class in a set using boost::shared_ptr使用 boost::shared_ptr 的集合中的不同模板类
【发布时间】:2011-10-04 16:16:40
【问题描述】:

我有一些设计问题,我想你们中的某个人可能有一些线索可以帮助我。

我试图将我的问题总结为这个简单的例子:

我有两个不同的类 DerivedOneDerivedTwo,它们继承自同一个 Base 类并共享一个方法的定义。
我有一组shared_ptr 指向client,其中一个对象来自两个不同的类DerivedOneDerivedTwo
因为我不想使用Base* 指针来保存这两个不同的类,所以我尝试制作客户端类模板。

但我有 2 个不同的班级,我不能将它们放在同一个集合中。
我以为shared_ptr 可以在不指定模板参数的情况下保存对象指针,但我错了,或者我不知道该怎么做。

我看到的另一个解决方案是将这 2 个不同的 client 分成 2 个不同的 set

提前感谢您的任何建议。

代码如下:

#include <iostream>
#include <set>
#include <boost/shared_ptr.hpp>

class Base
{
    public:
        virtual void test() = 0;
};

class Derived_one
    : public Base
{
    public:
        void test() {
            std::cout << "Derived One" << std::endl;
        }            
};

class Derived_two
    : public Base
{
    public:
        void test() {
            std::cout << "Derived Two" << std::endl;
        }
};

template <class temp_arg>
class Client
{
    public:        
        int test(){
            obj_.test();
        }

    protected:
        temp_arg obj_;

};

typedef Client<Derived_one> ClientOne;
typedef Client<Derived_two> ClientTwo;    

/// Here I don't want to specify any of the two previously declared client :
//typedef boost::shared_ptr<Client> Client_ptr;
typedef boost::shared_ptr<ClientOne> Client_ptr;

int main(int, const char**)
{
    std::set<Client_ptr> Clients_;
    Client_ptr client_(new ClientOne());

    Clients_.insert(client_);
    client_->test();

    return 0;
}

如果你想看一下真实代码:
https://github.com/gravitezero/Node/tree/experimental/src
Client对应connection
Base 类是 message
两个派生类是peplyrequest

【问题讨论】:

  • 为什么你不想使用基指针,它可以指向派生类对象?
  • 出于某些原因我不想使用指针。一个是我不想要dynamic_cast,这个指针可能指向dynamic_cast。另外,我想要一个组合,而不是客户端和派生对象之间的聚合。
  • @EtienneBrodu 如果您需要dynamic_cast,您的设计在另一个地方有问题,但在您向我们展示的地方却没有。指针在这里是一个非常合理的决定。

标签: c++ templates shared-ptr


【解决方案1】:

boost 共享模板指针不会执行任何没有常规指针(即将两种不同类型放入同一个集合)无法实现的魔法。每当使用模板时,您必须显式声明模板参数,因为模板所做的只是在编译时为您生成重复的代码。因此,当您使用模板时,编译器会简单地复制模板文件,就像您使用模板参数声明它一样多次。没有模板参数,没有重复,没有模板。至少这是我的理解,如果我错了,我相信C++警察会纠正我。

所以把两种不同的类型放到同一个集合中的方法就是屏蔽掉集合中的值。这样做的最基本的方法(也是不好的方法)是让你的 void 指针集合,然后根据上下文强制插入和提取。这很危险,因为这样您将通过指针访问内存,而不检查指针是否指向正确的内存量/格式(这是类型检查)。

所以真正的方法是按照 pmr 的建议。使您的集合成为基本类型指针的集合,并在从列表中插入和提取时使用动态转换在类型之间进行转换。动态转换将在运行时检查以确保您要转换的类型和要转换的类型是兼容的。 IE 如果你从 Derived1 转换到 Base,然后再从 Base 转换到 Derived1,这没关系。如果您从 Derived1 转换为 base,然后在指向同一内存的指针上从 base 转换为 Derived2,这将在运行时失败,因为 Derived2 和 Derived1 不兼容。

这个解释有点长,因为几个月前我遇到了同样的设计问题,直到我读了一些。

【讨论】:

    【解决方案2】:

    您为什么不想使用指向Base 实例的指针?如果您使用运行时多态,这是唯一的方法。如果您实际上不需要运行时多态性,那么虚拟成员函数是错误的方法。

    只需使用boost::scoped_ptr 或(更好的)C++11 std::unique_ptrClient 中保留base*

    如果您选择放弃运行时多态,您应该研究 CRTP 以实现静态多态。

    【讨论】:

    • 其实这两个派生类的一些成员是通用的,所以我用了这个Base类,我也把这个方法弄成virtual了,不过也不是很需要。我改变了这个设计很多次,我没想到它不再需要了。我不想要运行时多态性,因为我认为我可以在编译时处理这个问题。但我可能会绞尽脑汁。我从来没有听说过 CRTP,我要去拿一些文档,谢谢 :)
    • @EtienneBrodu,CRTP 无济于事,因为它依赖于不同的模板实例化,您已经发现这是一个问题。但无论如何这里有一个介绍:en.wikipedia.org/wiki/Curiously_recurring_template_pattern
    • @MarkRansom 但是他会得到一个类似层次结构的接口,并且可以简单地按值保留成员并将客户端作为模板。据我所知,这将解决问题,特别是因为他指出,不需要运行时操作。
    • 前几天我在How to simulate virtuality for method template发布了一个小的CRTP示例
    • @pmr,我阅读了一些关于 CRTP 的文档,我发现与我的问题最相关的案例是 this answer。你能告诉我我是对的吗?
    猜你喜欢
    • 1970-01-01
    • 2016-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多