我知道这个问题已经以许多不同的形式一再回答,并且也许是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()函数并返回其所需的类型。例如,我有一个用于拼接位的类,该位接受字节(无符号字符)作为输入:
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模板)。我的问题是,考虑到我的数据流/链接模式,是否还有其他“C++ ish”替代方法来实现我的设计,例如使用或继承STL中的某些内容(我几乎一无所知)?

还是在C++中根本无法实现?

最佳答案

目前的问题是您声明模板成员函数为virtual:

virtual T read();

...但是您没有定义它;这就是为什么会出现链接时错误的原因-BasicStream<bool>类的vtable需要指向一个函数,而没有一个函数。我很确定可以通过将其变为纯虚拟来解决该问题:
virtual T read() = 0;

...或提供默认定义。

09-07 06:56