避免dynamic_cast的替代设计
Alternative design to avoid dynamic_cast?
假设我有Archive
接口和File
接口。
- 每个
File
保证至少有std::string name
. - 每个
Archive
都可以std::vector<File*> Archive::list() const
其文件。 - 每个
Archive
都可以Archive::extract(std::vector<File*> files)
。
然后我有ZipArchive
和ZipFile
,ZipFile
包含存档文件中的偏移量和其他实现细节。然后是TarArchive
/TarFile
等等。这些中的每一个都用ZipFile
、TarFile
等实例填充std::vector<File*> list() const
。
list()
旨在让用户有机会选择要解压缩的文件。他们从该向量中选择元素,然后将该向量传递给extract()
。
此时,ZipArchive
需要假定它传递了正确的类型,并dynamic_cast<ZipFile*>(file)
访问实现详细信息。
这感觉很糟糕。这是可以接受的吗?有其他选择吗?
如注释中所建议的,您可以将提取界面从Archive
移动到File
。归档将返回std::vector<File*>
,但实际上每个对象都将是,例如,ZipFile
,并且将知道它属于哪个归档以及它的类型是什么,并且能够调用正确的提取方法。
因此,您可以拥有代码而无需检查存档类型:
struct File;
struct Archive {
virtual std::vector<File*> fileList() = 0;
};
struct File {
File(std::string name_) : name(name_) {}
virtual void extract() = 0;
std::string name;
};
struct ZipFile;
struct ZipArchive: public Archive {
void extractFile(ZipFile& file);
virtual std::vector<File*> fileList();
};
struct ZipFile: public File {
ZipArchive* archive;
virtual void extract() { archive->extractFile(*this); }
ZipFile(std::string name_, ZipArchive* archive_) : File(name_), archive(archive_) {}
};
完整示例:http://ideone.com/kAs5Jc
如果你想通过一次调用提取许多文件,它可能更困难,但你可以让存档的extractFile
只记住该文件,然后在Archive
类中使用特殊方法来一次提取所有记住的文件。我认为这甚至可以隐藏在一个相当简单的界面下。
您的ZipArchive
可以在其文件列表中搜索传递的指针。如果它在那里,它可以使用存储的指针(已经是 ZipFile
类型),也可以static_cast
传递给ZipFile
的指针(因为你已经证明了它的类型)。如果传递的指针不在列表中,则它显然不是此存档拥有的文件,因此您可以继续进行错误处理。
您还可以向每个File
添加类型 Archive*
的反向指针。具体的ZipArchive
实现可以通过简单的指针比较来检查其文件之一。
void ZipArchive::extract(std::vector<File*> files)
{
for (auto file : files)
{
if (file->archive() == this)
{
// one of my files
auto zipFile = static_cast<ZipFile*>(file);
// do something with zipFile
}
else
{
// file is owned by some other archive
}
}
}
class Archive {
public:
static int registerArchiveType(const std::string &name) {
// generate a unique int for the requested name archive type
// and store it in a map or whatever
return uniqueInt;
}
int archiveType() const;
protected:
Archive(int type) : _type(type) {}
private:
int _type;
public:
virtual extract(std::vector<File*> files);
// your implementation details
};
class File {
public:
int archiveType() { return _archType; }
protected:
// force implementations to pass the same type
// they received from the call to Archive::registerArchiveType
File() {}
void setArchiveType(const std::string &archiveType) {
// multiple calls to registerArchiveType return the
// same identifier if passed the same string
_archiveType = Archive::registerArchiveType(archiveType);
}
private:
int _archiveType;
};
然后在ZipArchive
实现中,在 extract
方法中,如果 archiveType
返回的 int 与为 Zip 存档类型注册的 int 相同,则可以执行static_cast而不是动态 int。
static const char* ZIP_TYPE = "zip";
// specialize your ZipFile making sure
// you pass the correct archive type identifier
// in the constructor
class ZipFile {
public:
ZipFile() : File() {
setArchiveType(ZIP_TYPE);
}
// bla bla
};
void ZipArchive::extract(std::vector<File*> files) {
for (int i = 0; i < files.count(); i++) {
if (files[i]->archiveType() == Archive::registerArchiveType(ZIP_TYPE)) {
ZipFile *zipFile = static_cast<ZipFile*>(files[i]);
// do something with zipFile
}
}
}
你需要分析你对待Archive
的方式:你是否需要在某些地方有一些由于不确定的类型而出现的常见行为?这将带来两种不同的设计选择,因此请谨慎选择。
正如评论中所说,您似乎不需要前者。
设File
表示文件句柄和ZipFile
,TarFile
其派生。然后,对于每种类型的文件,让Archive
处理它。
struct ZipFile
{
File handle;
// zip-specific implementation details
};
struct TarFile
{
File handle;
// tar-specific implementation details
};
class ZipArchive
{
public:
std::vector<ZipFile> list() const;
void extract(std::vector<ZipFile>);
private:
std::vector<ZipFile> archive;
};
TarArchive
也是如此.不再需要处理所有权、指针等;您还可以获得强大的类型安全性。
- 如何理解C++标准N3337中的expr.const.cast子句8
- C++Cast运算符过载
- 在成员dynamic_bitset上使用 boost::from_block_range 时出错,但在本地dynamic
- C++类中的二维"dynamic"数组?
- 错误:"cast"未命名类型void setCastDescription(std::string
- 通过使用 const-cast 的非常量引用来延长临时的寿命
- "(void) cast"与功能有什么区别 "__attributes__"来沉默未使用的参数警告?
- protobuf in C++ with dynamic binding for google::protobuf::M
- 警告的原因是什么:"when type is in parentheses, array cannot have dynamic size"?
- C++:"Expected '(' for function-style cast or type construction"错误
- 为什么选择 g++ 给予者:"error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]"
- CUDA 错误:"dynamic initialization is not supported for __device__, __constant__ and __shared__ variabl
- Gtk+ g_signal_connect() 和 C++ lambda 会导致"invalid cast"错误
- 如何修复'The procedure entry point SDL_RWclose could not be located in the dynamic link library'
- Shared_ptr cast vs static_cast speed
- 在 iOS 上使用 Aruco 构建 OpenCV 时"Functional-style cast from id to double is not allowed"
- "The ordinal 344 could not be located in the dynamic link library"
- 覆盖 CAST 运算符(我认为它被称为向下转换)
- Dynamic Cast C++ Fail
- dynamic-cast-c++dynamic_cast错误处理