在派生 类 中继承和使用成员变量而无需每次都强制转换的模式
Pattern for inheriting and using a member variable in derived classes without casting each time
我有一个基地class:
class Base{
protected:
Storage* _storage;
virtual void createStorage(){
delete storage;
_storage = new Storage();
}
void exampleUseOfBaseStorage(){
_storage->baseData++; //some complex calculation
}
}
struct Storage{
int baseData;
}
每个派生 class 都有自己的存储类型:
struct DerivedStorage : Storage{
int derivedData;
}
在导出class,
class Derived{
protected:
virtual void createStorage() override{
delete storage;
_storage = new DerivedStorage();
}
}
因此 _storage 可用于所有基础 class 成员和派生 class 成员。
缺点是,因为对于每个派生 class,我必须将存储类型强制转换为 DerivedStorage,或者派生 class 使用的任何存储类型,键入强制转换语句非常繁琐每一次。有没有优雅的解决方法?
我的解决方案是再多一个 DerivedStorage 类型的变量,然后在派生的 classes 成员函数中使用它。例如:
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
}
}
是否有更优雅的模式可用于此用例?
因为方法Base::exampleUseOfBaseStorage
是受保护的,这个方法的调用者仅限于导数classes(和Base)。
因此,如果每个导数 class 都将每个存储作为自己的成员,则它们可以将其用作 Base::exampleUseOfBaseStorage
的参数。
class Base{
protected:
//(This will be called from Drived)
void exampleUseOfBaseStorage( Storage *storage );
};
我会将转换放入成员函数中,而不是使用数据成员。成员函数在每个派生中可以有相同的名字class。如果实在不想在每个派生中都打出函数定义class,可以使用CRTP来实现
使用数据成员无法安全地使用 rule-of-zero 特殊成员函数方法,您必须在每个派生的 class.[=13= 中显式实现所有这些方法]
我假设显示的代码只是缩写,但基础 class 有同样的问题。如果它使用 std::unique_ptr<Storage>
而不是原始指针,它可以简单地遵循 rule-of-zero 而不必费心实现任何特殊成员函数,但正如问题中所写,特殊成员函数需要明确定义以避免在 class 为 copied/moved 时导致 UB。 (而且 std::unique_ptr
会使 createStorage
exception-safe,目前不是。)
我觉得问题应该是“你真的需要导出存储吗?”。大多数时候 composition/aggregation 应该足够了。
即使您需要计算派生存储和基础存储的数据,您仍然可以在派生中访问它们class
构图示例
#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's 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;
}
我有一个基地class:
class Base{
protected:
Storage* _storage;
virtual void createStorage(){
delete storage;
_storage = new Storage();
}
void exampleUseOfBaseStorage(){
_storage->baseData++; //some complex calculation
}
}
struct Storage{
int baseData;
}
每个派生 class 都有自己的存储类型:
struct DerivedStorage : Storage{
int derivedData;
}
在导出class,
class Derived{
protected:
virtual void createStorage() override{
delete storage;
_storage = new DerivedStorage();
}
}
因此 _storage 可用于所有基础 class 成员和派生 class 成员。 缺点是,因为对于每个派生 class,我必须将存储类型强制转换为 DerivedStorage,或者派生 class 使用的任何存储类型,键入强制转换语句非常繁琐每一次。有没有优雅的解决方法?
我的解决方案是再多一个 DerivedStorage 类型的变量,然后在派生的 classes 成员函数中使用它。例如:
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
}
}
是否有更优雅的模式可用于此用例?
因为方法Base::exampleUseOfBaseStorage
是受保护的,这个方法的调用者仅限于导数classes(和Base)。
因此,如果每个导数 class 都将每个存储作为自己的成员,则它们可以将其用作 Base::exampleUseOfBaseStorage
的参数。
class Base{
protected:
//(This will be called from Drived)
void exampleUseOfBaseStorage( Storage *storage );
};
我会将转换放入成员函数中,而不是使用数据成员。成员函数在每个派生中可以有相同的名字class。如果实在不想在每个派生中都打出函数定义class,可以使用CRTP来实现
使用数据成员无法安全地使用 rule-of-zero 特殊成员函数方法,您必须在每个派生的 class.[=13= 中显式实现所有这些方法]
我假设显示的代码只是缩写,但基础 class 有同样的问题。如果它使用 std::unique_ptr<Storage>
而不是原始指针,它可以简单地遵循 rule-of-zero 而不必费心实现任何特殊成员函数,但正如问题中所写,特殊成员函数需要明确定义以避免在 class 为 copied/moved 时导致 UB。 (而且 std::unique_ptr
会使 createStorage
exception-safe,目前不是。)
我觉得问题应该是“你真的需要导出存储吗?”。大多数时候 composition/aggregation 应该足够了。
即使您需要计算派生存储和基础存储的数据,您仍然可以在派生中访问它们class
构图示例
#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's
我将所有成员更改为 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;
}