我有一个基类:
class Base{
protected:
Storage* _storage;
virtual void createStorage(){
delete storage;
_storage = new Storage();
}
void exampleUseOfBaseStorage(){
_storage->baseData++; //some complex calculation
}
}
struct Storage{
int baseData;
}
每个派生类都有自己的存储类型:
struct DerivedStorage : Storage{
int derivedData;
}
在派生类中,
class Derived{
protected:
virtual void createStorage() override{
delete storage;
_storage = new DerivedStorage();
}
}
因此,_storage可以用于所有基类成员和派生类成员。缺点是,因为对于每个派生类,我必须将存储类型转换为DerivedStorage,或者派生类使用的任何类型的存储,所以每次输入cast语句都非常繁琐。有一个优雅的方法绕过它吗?
我的解决方案是只有一个类型为DerivedStorage的变量,只需在派生类成员函数中使用。例:
class Derived{
protected:
DerivedStorage* _ds = nullptr;
virtual void createStorage() override{
delete storage;
_storage = new DerivedStorage();
_ds = _storage; // Use _ds in all member functions of Derived instead of _storage
}
void exampleUseOfDerivedStorage(){
_ds->derivedData++; //some complex calculation
}
}
是否有更优雅的模式可用于此用例?
发布于 2022-03-03 02:28:37
我认为问题应该是:“你真的需要储存出来吗?”大多数时间组合/聚合应该足够了。
即使需要计算派生存储和基存储中的数据,也可以在派生类中访问它们。
构图示例
#include<string>
#include<iostream>
#include<vector>
#include<algorithm>
struct Storage{
int baseData{};
};
struct DerivedStorage{
int derivedData{};
};
class Base{
public:
Storage _storage;
void exampleUseOfBaseStorage(){
_storage.baseData++; //some complex calculation
}
};
class Derived: Base{
public:
DerivedStorage _ds;
void exampleUseOfDerivedStorage(){
_ds.derivedData++; //some complex calculation
std::cout << "baseData: " << _storage.baseData << std::endl;
std::cout << "derivedData: " << _ds.derivedData << std::endl;
}
};
int main(){
Derived d;
d.exampleUseOfDerivedStorage();
return 0;
}
CRTP示例
下面的示例详细介绍了user17732522关于CRTP使用的回答。
我将所有成员更改为public,因为示例没有详细说明何时调用createStorage()
#include<string>
#include<iostream>
#include<vector>
#include<algorithm>
struct Storage{
int baseData{};
};
struct DerivedStorage : Storage{
int derivedData{};
};
template<typename DS>
class Base{
public:
Storage* _storage;
virtual void createStorage(){
delete _storage;
_storage = new Storage();
}
DS* getStorage(){
return static_cast<DS*>(_storage);
}
void exampleUseOfBaseStorage(){
_storage->baseData++; //some complex calculation
}
};
class Derived: Base<DerivedStorage>{
public:
virtual void createStorage() override{
delete _storage;
_storage = new DerivedStorage();
}
void exampleUseOfDerivedStorage(){
getStorage()->derivedData++; //some complex calculation
std::cout << "derivedData: " << getStorage()->derivedData << std::endl;
}
};
int main(){
Derived d;
d.createStorage();
d.exampleUseOfDerivedStorage();
return 0;
}
发布于 2022-03-03 00:44:35
我会将转换放入成员函数中,而不是使用数据成员。成员函数可以在每个派生类中具有相同的名称。如果您真的不想在每个派生类中键入函数定义,可以使用CRTP来实现它。
使用数据成员使得无法安全地使用对特殊成员函数的零规则方法,您必须在每个派生类中显式地实现它们。
我假设显示的代码只是缩写,但是基类也有同样的问题。如果它使用的是std::unique_ptr<Storage>
而不是原始指针,那么它可以简单地遵循零的规则,而不必费心地实现任何特殊的成员函数,但是当它写在问题中时,需要显式地定义特殊的成员函数,以避免在类被复制/移动时导致UB。(此外,std::unique_ptr
还将使createStorage
异常安全,而目前并非如此。)
发布于 2022-03-03 00:20:38
由于方法Base::exampleUseOfBaseStorage
是受保护的,因此该方法的调用方仅限于派生类(和基)。
因此,如果每个派生类都将每个存储作为自己的成员,则可以将其用作Base::exampleUseOfBaseStorage
的参数。
class Base{
protected:
//(This will be called from Drived)
void exampleUseOfBaseStorage( Storage *storage );
};
https://stackoverflow.com/questions/71332646
复制相似问题