在派生 类 中继承和使用成员变量而无需每次都强制转换的模式

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

构图示例

Live Demo

#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的使用。

Live Demo

我将所有成员更改为 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;
}