C++虚拟模板方法
C++ virtual templated method
我知道这个问题已经以许多不同的形式一次又一次地得到回答,并且可能是 C++ 设计中最令人困惑的地方之一,但我在尝试学习这门语言多年后,在纯语言中做了很多事情C 在 C++ 世界中被认为是完全非法的(想想函数指针杂耍)。在放弃并转向另一种语言之前,我想尝试让自己了解 C++ 的思维方式。
我刚刚开始了一个项目,其中最基本的组件是流 class,为此我希望它是通用的:它将流式传输哪种数据取决于它的子classes.
template <typename T>
class BasicStream {
protected:
T *buffer;
unsigned int bufferSize;
unsigned int bufferPos;
bool streamEnd=false;
public:
virtual T read();
};
我的想法是将对象链接在一起,就像某个 class 的一个对象的输出被另一个 class 的另一个对象直接读取一样。但要使其工作,所有对象都必须能够接受通用 read()
函数和 return 所需的类型。例如,我有一个 class 来拼接接受字节(无符号字符)作为输入的位:
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();
};
它 return 是一个 bool
类型,需要从 BasicStream
派生并具有 <unsigned char>
return 类型的任何 class 作为输入.我的想法是让输入与数据源完全无关;无论是文件、互联网流,还是内存中的某个位置;全部包裹在来自 BasicStream<unsigned char>
.
的 classes 周围
一个例子是 FileReader
class,用于处理 a/synchronous 文件加载:
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>
派生的 class,它应该识别模板函数。
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
错误,它无法 link:
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
我已经通过其他 Whosebug 问题了解到我的代码在 C++ 中是不可能的,因为它缺乏运行时多态性(编译器无法决定 subclass 在 BasicStream
的哪个模板调用运行)。我的问题是,考虑到我的数据 streaming/chaining 模式,是否还有其他 "C++ish" 替代方案来实现我的设计,例如使用或子 class 来自 STL 的东西(我几乎知道什么都没有)?
或者它在 C++ 中根本无法实现?
当然你应该使用标准库中的流。
您提到的唯一重要功能是从流中读取布尔值,这是通过 std::basic_istream 的适当 operator>>
完成的。如果您有额外的特殊需求,您可以在临时流子类中覆盖它。
问题中的代码具有人为限制,即假装流是某种类型的值的同构序列,具有非常不方便的模板参数。
实际上,流应该被视为任何类型的值的源或汇(您可以在任何位置读取或写入任何内容)或字节序列(读取、写入以及从其他类型的值转换为其他类型的值)。标准库流通过重载和模板化的 operator>> 和 operator<< 以及基于字符的低级 I/O 和位置记帐。
目前的问题是你将模板成员函数声明为virtual:
virtual T read();
...但是你没有定义它;这就是为什么你会得到一个 link 时间错误 - BasicStream<bool>
class 的 vtable 需要一个函数来指向,但没有一个。我很确定可以通过将其设为纯虚拟来解决该问题:
virtual T read() = 0;
...或提供默认定义。
我知道这个问题已经以许多不同的形式一次又一次地得到回答,并且可能是 C++ 设计中最令人困惑的地方之一,但我在尝试学习这门语言多年后,在纯语言中做了很多事情C 在 C++ 世界中被认为是完全非法的(想想函数指针杂耍)。在放弃并转向另一种语言之前,我想尝试让自己了解 C++ 的思维方式。
我刚刚开始了一个项目,其中最基本的组件是流 class,为此我希望它是通用的:它将流式传输哪种数据取决于它的子classes.
template <typename T>
class BasicStream {
protected:
T *buffer;
unsigned int bufferSize;
unsigned int bufferPos;
bool streamEnd=false;
public:
virtual T read();
};
我的想法是将对象链接在一起,就像某个 class 的一个对象的输出被另一个 class 的另一个对象直接读取一样。但要使其工作,所有对象都必须能够接受通用 read()
函数和 return 所需的类型。例如,我有一个 class 来拼接接受字节(无符号字符)作为输入的位:
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();
};
它 return 是一个 bool
类型,需要从 BasicStream
派生并具有 <unsigned char>
return 类型的任何 class 作为输入.我的想法是让输入与数据源完全无关;无论是文件、互联网流,还是内存中的某个位置;全部包裹在来自 BasicStream<unsigned char>
.
一个例子是 FileReader
class,用于处理 a/synchronous 文件加载:
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>
派生的 class,它应该识别模板函数。
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
错误,它无法 link:
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
我已经通过其他 Whosebug 问题了解到我的代码在 C++ 中是不可能的,因为它缺乏运行时多态性(编译器无法决定 subclass 在 BasicStream
的哪个模板调用运行)。我的问题是,考虑到我的数据 streaming/chaining 模式,是否还有其他 "C++ish" 替代方案来实现我的设计,例如使用或子 class 来自 STL 的东西(我几乎知道什么都没有)?
或者它在 C++ 中根本无法实现?
当然你应该使用标准库中的流。
您提到的唯一重要功能是从流中读取布尔值,这是通过 std::basic_istream 的适当 operator>>
完成的。如果您有额外的特殊需求,您可以在临时流子类中覆盖它。
问题中的代码具有人为限制,即假装流是某种类型的值的同构序列,具有非常不方便的模板参数。 实际上,流应该被视为任何类型的值的源或汇(您可以在任何位置读取或写入任何内容)或字节序列(读取、写入以及从其他类型的值转换为其他类型的值)。标准库流通过重载和模板化的 operator>> 和 operator<< 以及基于字符的低级 I/O 和位置记帐。
目前的问题是你将模板成员函数声明为virtual:
virtual T read();
...但是你没有定义它;这就是为什么你会得到一个 link 时间错误 - BasicStream<bool>
class 的 vtable 需要一个函数来指向,但没有一个。我很确定可以通过将其设为纯虚拟来解决该问题:
virtual T read() = 0;
...或提供默认定义。