构建STL向量的接口

时间:2011-12-29 05:53:44

标签: c++ stl

我正在为C ++ STL容器向量构建一个包装器(然后会有更多的功能,然后向量可以提供(加载,保存等),因此它需要是一个包装器。)

我的包装器的消费者需要迭代通过元素,如果我公开STL迭代器,稍后更改实现将需要我的调用者重新编译(加上我觉得我打破了封装)。

我想创建一个只返回基本类型的接口,以确保在更改实现时客户端不需要重新编译。我正在考虑将矢量大小暴露为整数(类似于MFC CArray的工作方式),并且还重载了[]运算符,调用者可以通过这种方式循环向量。

我的问题:

  1. 如果我想为矢量大小返回一个int,那对size_type有什么作用? Size_type似乎不应该在接口中暴露,因为如果它改变了调用者将需要重新编译。我很乐意施加某种限制,如果事实证明size_type可以大于整数(我不指望我会有那么多元素!)
  2. 使用[]运算符循环向量比使用迭代器
  3. 差得多

    编辑:删除了'generic'这个词 - 这与模板无关,它只是一个类。还澄清了“公开原始类型”是指返回int而不是数据成员本身的方法。

4 个答案:

答案 0 :(得分:2)

您可以定义客户端将引用的singelton ifc(纯虚拟)类。我认为这种设计模型被称为“Singelton工厂方法”。 我希望我的长篇答案可以帮助你:)。

只要您不更改公共接口(方法列表),如果您更改代码,您的客户端就不需要重新编译。

类似的东西:

myClassIfc.h:

Class myClassIfc
{
public:
    virtual ~myClassIfc();    

    ///// list all your pure virtual public ifc methods here ////
    void m_zRunMyMethod(int nNumber) = 0;
    int m_nSize() = 0;

    static myClassIfc* ms_pGetImplObj();      

protected:        
    myClassIfc();
    static myClassIfc* ms_pImplObj;
}

inline myClassIfc* myClassIfc::ms_pGetImplObj()
{
    return ms_pImplObj;
}

myClassIfc.cpp:

#include myClassIfc.h

myClassIfc::myClassIfc()
{
}
myClassIfc::~myClassIfc()
{
}

myClass.h - 实现纯虚拟类

Class myClass: public myClassIfc
{
public:
    virtual ~myClass();    

    void m_zRunMyMethod(int nNumber);
    int m_nSize();

    static void ms_zCreate();
    static void ms_zDestroy();

protected:        
    myClass();

private:
    vector<int> myInternalVector;
}

myClass.cpp:

#include myClass.h

void myClass::m_zRunMyMethod(int nNumber)
{
   /// your action
   printf("%d\n", nNumber);
}

int myClass::m_nSize()
{
   return int(myInternalVector.size());
}

void myClass::ms_zCreate()
{
    if (NULL != ms_pImplObj)
    {
        return;
    }
    ms_pImplObj = (myClass*) new myClass();   
}

void myClass::ms_zDestroy()
{
    if (NULL == ms_pImplObj)
    {
        return;
    }
    delete ms_pImplObj;
    ms_pImplObj = NULL;
}

现在经过上述长期基础设施工作后,您的客户需要使用

#include myClassIfc.h


void main(void)
{
    myClassIfc::ms_pGetImplObj()->m_zRunMyMethod(5);
    myClassIfc::ms_pGetImplObj()->m_nSize();         
}

我上面没有列出的唯一内容是你的内存管理,这意味着谁自己创建单例对象(调用派生类的ms_zCreate()静态API)。 您可以从其他地方或直接从您的代码中调用它。

请注意,您可以将上述ifc方法操作为非singelton实现。 只要ifc类没有改变,如果修改了derive(implementation)类,客户端代码就不需要重新编译。

答案 1 :(得分:1)

我不建议实际使用索引和运算符[]。不要直接暴露向量迭代器,而是在类中创建最初等同于向量迭代器的迭代器typedef,但是您可以将其更改为您想要的任何内容,而无需客户端更改代码。然后你可以使用普通的begin / end / find方法。

答案 2 :(得分:1)

在现代C ++设计面前,有很多你要求的东西。我会把你推荐给Herb Stutter和Andrei Alexandrescu的书C++ Coding Standards。在课程设计上检查这些章节标题:

Class Design and Inheritance
32. Be clear what kind of class you’re writing. 56
33. Prefer minimal classes to monolithic classes. 57
34. Prefer composition to inheritance. 58
35. Avoid inheriting from classes that were not designed to be base classes. 60
38. Practice safe overriding. 66
39. Consider making virtual functions nonpublic, and public functions nonvirtual. 68
41. Make data members private, except in behaviorless aggregates (C-style structs). 72
42. Don’t give away your internals. 74
44. Prefer writing nonmember nonfriend functions. 79

特别注意#44。而不是重写STL向量,创建在STL迭代器上运行的非成员函数。这也与#35有关。 STL类实际上并不是基类。 #33是我在你的评论中畏缩的原因,你想要在STL向量中添加“加载”和“保存”功能。这些函数听起来应该是非成员函数。

哦,你真的想过这个:

  

我想创建一个只暴露原始类型的接口,以确保在我更改实现时客户端不需要重新编译。我在考虑将矢量大小暴露为整数

一方面,您希望使用合成来隐藏另一个类中的矢量。好的。不是最灵活的设计,但您可能需要与非C ++代码进行交互。但是,您希望将类的数据成员公开为接口的一部分。这可能会产生客户需要重新编译的问题。这没有意义。您要么完全封装,要么不关心频繁的重新编译。这是什么? (并且请不要在MFC CArray类上进行任何模型化。这很糟糕。)

答案 3 :(得分:0)

你说“我想创建一个只暴露的界面 原始类型,以确保客户端不需要重新编译。“只是 反之亦然;如果你想避免重新编译,你需要做 用户定义的所有类型,使用编译防火墙的习惯用法。 在迭代器的情况下,这可能具有不可接受的性能 惩罚:迭代器成语需要深层复制,而事实就是如此 没有任何内联可能也会影响优化。

我认为完全避免重新编译可能是不合理的 C ++的工作方式。更重要的是你要避免要求 如果更改实现,则修改客户端代码。在这 例如,您想要准确定义您想要的内容 保证;如果你认为你的类中的迭代器必须是随机的 访问迭代器,然后是std::vector<>::iterator的typedef 可能足够了;如果你想要保证的是前向迭代 (为了让以后的实施更加自由),你想要 考虑将std::vector<>::iterator包装在您自己的迭代器中, 这只会暴露您愿意担保的业务 所有未来版本的支持。

请注意,如果您以后决定使用不实现的实现 支持随机访问迭代器,您也不能支持[]。 支持[]对未来的实现施加更多限制 支持前向迭代器。