【发布时间】:2016-09-28 12:37:51
【问题描述】:
我知道这个问题已经一次又一次地以许多不同的形式得到了回答,这可能是 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++ 中根本无法实现?
【问题讨论】:
-
您的代码没有出现您在标题中提到的问题。模板基类可能会引入一个虚函数(甚至是依赖于类模板类型参数的虚函数)。实例化模板后,vtable 中只有一个定义明确的条目。
-
您的问题可能出在您未显示的代码中。
-
至少,请始终显示确切的编译器错误。包含足够多的实现以允许其他人尝试编译也是非常有帮助的。
-
@MVittiS:在 BasicStream 中,更改 'virtual T read();'到'虚拟T读取()= 0;'并为其添加一个虚拟析构函数。这将使 BasicStream 成为一个抽象类,编译器不会期望它的实现。除非您还没有在派生类中提供用于读取的实现,否则该错误不应出现在该更改之后。
-
这是一个经典的初学者错误;如果有人没有找到与此问题重复的较早问题,我会感到惊讶。阅读“纯虚函数”。
标签: c++ class templates polymorphism virtual