C ++虚拟模板化方法

时间:2016-05-30 15:59:35

标签: c++ class templates polymorphism virtual

我知道这个问题已经一次又一次地以多种不同的形式得到了回答,也许是C ++设计中最令人困惑的问题之一,但我试图在以后学习这门语言多年来在C语言中做很多事情在C ++世界中被认为是非法的(想想函数指针杂耍)。在放弃和使用其他语言之前,我想尝试自学C ++思维模式。

我刚刚开始了一个项目,其中最基本的组件是流类,为此我希望它是通用的:它将流式传输到它的子类的数据类型。

template <typename T>
class BasicStream {
protected:
    T *buffer;
    unsigned int bufferSize;
    unsigned int bufferPos;
    bool streamEnd=false;
public:
    virtual T read();
};

我的想法是将对象链接在一起,就像某个类的一个对象的输出被另一个类的另一个对象直接读取一样。但为了使其工作,所有对象必须能够接受通用read()函数并返回其所需类型。例如,我有一个类来拼接接受字节(unsigned char)作为输入的位:

class BitExtractor : public BasicStream<bool> {
private:
    unsigned char bitMask;
    unsigned char byte;
    BasicStream<unsigned char> &byteSource;

public:
    BitExtractor(BasicStream<unsigned char> &source);
    virtual bool read();
};

它返回bool类型,需要任何派生自BasicStream且具有<unsigned char>返回类型作为输入的类。我的想法是使输入与数据源完全无关;无论是文件,互联网流,还是记忆中的某些位置;所有这些都包含在BasicStream<unsigned char>派生的类中。

一个例子是FileReader类,用于处理/同步文件加载:

class FileReader : public BasicStream<unsigned char> {
protected:
    FILE *file;
    bool asyncFlag;
    bool asyncOpReady;
    bool fileEnded;
    pthread_t asyncThread;
    unsigned int lastRead;
public:
    FileReader(char *fileName,int bufferSize=1024,bool asyncRead=false);
    ~FileReader();

    virtual unsigned char read();

private:
    typedef struct {
        unsigned int amount;
        unsigned int *read;
        unsigned char *buffer;
        FILE *file;
        bool *flag;
        bool *flagStreamEnd;
    } TData;
    static void AsyncRead(void *data);
};

现在,我想假设我想使用FileReader创建一个BitExtractor作为数据源。

BitExtractor bx=BitExtractor(FileReader("SomeFile.abc"));
bool firstBit = bx.read();

在内部,BitExtractor正在调用FileReader read()方法。我的假设是,由于FileReader是一个派生自BasicStream<unsigned char>的类,它应该识别模板化函数。

BitExtractor::BitExtractor(BasicStream<UInt8> &source):bitMask(128),byteSource(source){}

bool BitExtractor::read(){
    bool bit=byte&bitMask;
    if(streamEnd==false){
        bitMask>>=1;
        if(bitMask==0){
            try {
                byte=byteSource.read();
                bitMask=128;
            } catch (...) {
                streamEnd=true;
            }
        }
    }
    else{
        throw "Bytesource has ended!\n";
    }

    return bit;
}

即使它已编译,但由于vtable错误而无法链接:

Undefined symbols for architecture x86_64:
  "BasicStream<bool>::read()", referenced from:
      vtable for BasicStream<bool> in BitIO.o
  "BasicStream<unsigned char>::read()", referenced from:
      vtable for BasicStream<unsigned char> in FileIO.o

我已经通过其他StackOverflow问题了解到我的代码在C ++中是不可能的,因为它缺乏运行时多态性(编译器无法决定子类在运行时调用哪个BasicStream模板)。我的问题是,鉴于我的数据流/链接模式,还有其他更多的&#34; C ++ ish&#34;实现我的设计的替代方法,比如使用或继承STL中的东西(我几乎什么都不知道)?

或者它只是在C ++中无法实现?

2 个答案:

答案 0 :(得分:5)

问题在于您将模板成员函数声明为虚拟:

virtual T read();

...但你没有定义它;这就是你得到链接时错误的原因 - BasicStream<bool>类的vtable需要一个指向的函数,而没有一个。我很确定这个问题可以通过使其成为纯虚拟来解决:

virtual T read() = 0;

...或提供默认定义。

答案 1 :(得分:1)

当然,您应该使用标准库中的流。

您提到的唯一非常重要的功能是从流中读取一个bool,这是通过 std :: basic_istream 的相应operator>>来完成的。如果您有特殊需求,可以在ad hoc流子类中覆盖它。

问题中的代码具有假设流是特定类型的同构序列的人为限制,具有非常不方便的模板参数。 实际上,流应该被视为任何类型的值的源或接收器(您可以在任何位置读取或写入任何内容)或字节序列(从其他类型的值读取,写入和转换)。标准库流做两者,使用重载和模板化运算符&gt;&gt;和运算符&lt;&lt;以及基于字符的低级I / O和头寸会计。