0

我正在实现一个通用接口,可以桥接某些基类的不同实现。

接口实用程序编写如下:

// InterfaceUtils.h
//

// Base object class
class IBaseObject {
    virtual ~IBaseObject() = default;
};

// Unknown object adapter
class UnknownObject
{
public:
    UnknownObject() = default;
    UnknownObject(const UnknownObject& from);
    UnknownObject(UnknownObject&& from) noexcept;
    explicit UnknownObject(const IBaseObject& from);
    explicit UnknownObject(IBaseObject* from)
    {
         SetPointer(from);
    }
    virtual ~UnknownObject()
    {
         DeletePointer();
    }

    template<class Interface>
    const Interface* RetrieveInterface() const
    {
        if (!m_pObject)
            return nullptr;
        return dynamic_cast<const Interface*>(m_pObject);
    }
    template<class Interface>
    Interface* RetrieveInterface()
    {
         if (!m_pObject)
             return nullptr;
         return dynamic_cast<Interface*>(m_pObject);
    }
    UnknownObject& operator=(const UnknownObject& other);
    UnknownObject& operator=(UnknownObject&& other) noexcept;
    [[nodiscard]] bool IsNull() const
    {
        return m_pObject == nullptr;
    }
    void SetNull();
protected:
    [[nodiscard]] virtual const IBaseObject* GetPointer() const
    {
        return m_pObject;
    }
    virtual IBaseObject* GetPointer()
    {
        return m_pObject;
    }
    void SetPointer(ISymplektBaseObject* pObject)
    {
        m_pObject = pObject;
    }
    void DeletePointer() const
    {
        // Q: Unsafe for memory leaks ?
        // delete m_pObject;
    }
private:

    //>! stored pointer to object
    IBaseObject* m_pObject = nullptr;
};

// Generic object interface template
template <class Interface>
class ObjectInterface : public UnknownObject
{
public:
    explicit ObjectInterface(const IBaseObject& other)
        : UnknownObject(other) { }
    explicit ObjectInterface(IBaseObject* other)
        : UnknownObject(other) { }
    explicit ObjectInterface(const UnknownObject& other)
        : UnknownObject(other.RetrieveInterface<Interface>() != nullptr ? other : UnknownObject()) {  }
    explicit ObjectInterface(UnknownObject&& other) noexcept
        : UnknownObject(other.RetrieveInterface<Interface>() != nullptr ? std::move(other) : UnknownObject()) {  }
   ObjectInterface(const ObjectInterface<Interface>& other)
        : UnknownObject(other) { }
   ObjectInterface(ObjectInterface<Interface>&& other) noexcept
        : UnknownObject(std::move(other)) { }
   ~ObjectInterface() override
    {
          UnknownObject::~UnknownObject();
    }
    ObjectInterface& operator=(const ObjectInterface<Interface>& other)
    {
          return UnknownObject::operator=(other);
    }
    Interface* operator->()
    {
          return GetInterface();
    }
    const Interface* operator->() const
    {
          return GetInterface();
    }
    Interface* GetInterface()
    {
          return RetrieveInterface<Interface>();
    }
    const Interface* GetInterface() const
    {
          return RetrieveInterface<Interface>();
    }
};

如果有什么用,我也会写 *.cpp 的内容:

// InterfaceUtils.cpp
//
#include "InterfaceUtils.h"

void UnknownObject::DeleteInterface(UnknownObject& obj)
{
    if (obj.IsNull())
        return;

    obj.SetNull();
}

UnknownObject::UnknownObject(const UnknownObject& from)
{       
    if (auto* fromObj = from.GetPointer(); fromObj != nullptr)
    {
        SetPointer(const_cast<IBaseObject*>(fromObj));
        return;
    }

    SetPointer(nullptr);
}

UnknownObject::UnknownObject(UnknownObject&& from) noexcept
{
    SetPointer(from.GetPointer());
    from.SetPointer(nullptr);
}

UnknownObject::UnknownObject(const IBaseObject& from)
{
    auto* pObject = &from;
    SetPointer(const_cast<IBaseObject*>(pObject));
}

UnknownObject& UnknownObject::operator=(const UnknownObject& other)
{
    if (this == &other)
        return *this;       
    
    if (auto* fromObj = other.GetPointer(); fromObj != nullptr)
        SetPointer(const_cast<IBaseObject*>(fromObj));
    else
        SetPointer(nullptr);
    
    return *this;
}

UnknownObject& UnknownObject::operator=(UnknownObject&& other) noexcept
{
    DeletePointer();

    SetPointer(other.GetPointer());
    other.SetPointer(nullptr);
    return *this;
}

void UnknownObject::SetNull()
{
    DeletePointer();
    m_pObject = nullptr;
}

classes 的目的IBaseObject是能够扩展到不同的实现UnknownObject,例如:ObjectInterface<>IBaseObject

class ITestClass : public IBaseObject
{
public:
    ITestClass() : IBaseObject() {}
    ITestClass(const double& val)
       : IBaseObject(), m_Value(val) { }

    void SetValue(const double& val)
    {
        m_Value = val;
    }

    [[nodiscard]] double GetValue() const
    {
        return m_Value;
    }

    [[nodiscard]] virtual double ProcessValue() const = 0;

protected:
    double m_Value = 0.0;
};

//
// test class implementation returning m_Value^2 with ProcessValue
//
class TestClassSquareImpl : public ITestClass
{
public:
    ~TestClassSquareImpl() override
    {}

    TestClassSquareImpl(const double& val)
        : ITestClass(val) {}

    [[nodiscard]] double ProcessValue() const override
    {
        return m_Value * m_Value;
    }
};

//
// test class implementation returning sqrt(m_Value) with ProcessValue
//
class TestClassSqrtImpl : public ITestClass
{
public:
    ~TestClassSqrtImpl() override
    {}

    TestClassSqrtImpl(const double& val)
        : ITestClass(val)
    {
    }

    [[nodiscard]] double ProcessValue() const override
    {
        return sqrt(m_Value);
    }
};

使用如下:

const double dataValue = 3.0;
const auto testObjSqr = TestClassSquareImpl(dataValue);
const auto testObjSqrt = TestClassSqrtImpl(dataValue);

const auto iTestObjSqr = ObjectInterface<ITestClass>(testObjSqr);
const auto iTestObjSqrt = ObjectInterface<ITestClass>(testObjSqrt);

std::cout << iTestObjSqr->ProcessValue() << "\n";    // out: 9.0
std::cout << iTestObjSqrt->ProcessValue() << "\n";   //  out: 1.73205080...

问题是:根据我以前的经验,方法UnknownObject::DeletePointer应该删除 rawm_pObject以避免内存泄漏,但是这样做时会引发异常,因为派生实例的内存块即将被释放。m_pObject不删除指针是否安全?UnknownObject销毁后还是会被删除吗?

inb4:我已经尝试过使用std::unique_ptrstd::shared_ptr导致无法从派生实现(例如TestClassSquareImpl::ProcessValueor ITestClass::GetValue)访问方法。

编辑:抛出的异常free是:Invalid address specified to RtlValidateHeap( 0000016F756B0000, 00000004B0AFF988 )。我在 gtest 中运行示例时第一次遇到它,但是当程序超出范围时会发生同样的事情,从而导致DeletePointer调用。

4

1 回答 1

0

我接受了@Frank 的建议,删除了const_cast's 并实现了一个Clone带有抽象声明的方法:

[[nodiscard]] virtual IBaseObject* Clone() const = 0;

IBaseObject其中必须在派生类中实现:

[[nodiscard]] IBaseObject* Clone() const override
{
    return (new TestClassSquareImpl(*this));
}

和类似地在TestClassSqrtImpl. 该Clone方法在复制构造和复制分配中是必需的(每当访问和复制具有自己实例的不同接口或不同实例本身时,例如SetPointer(fromObj->Clone());:)

此外,还有多次删除造成的

~ObjectInterface() override
{
    UnknownObject::~UnknownObject();
}

调用它的基本析构函数。

通过这些调整,delete m_pObject不会抛出。

需要注意的是,put 将std::shared_ptr<IBaseObject> m_pObject;访问任何成员的范围限制为IBaseObject成员。如果我错了,请纠正我,但在这种情况下,m_Value需要将其定义为IBaseObject

非常感谢您的见解。

编辑:我发现我正在重新发明轮子。专门的STL 转换可以轻松地在测试示例中执行向下转换:

#include <iostream>
#include <memory>
#include <cmath>

class Base
{
public:
    Base(const double value)
        : m_Value(value) {}

    double& GetValue()
    {
        return m_Value;
    }

    void SetValue(const double value)
    {
        m_Value = value;
    }

    [[nodiscard]] virtual double ProcessValue() const = 0;
protected:
    double m_Value{ 0.0 };
};

class DerivedA : public Base
{
public:
    DerivedA(const double value)
        : Base(value) {}

    [[nodiscard]] double ProcessValue() const override
    {
        return m_Value * m_Value + m_SpecializedValueForA;
    }

private:
    double m_SpecializedValueForA = 1.0;
};

class DerivedB : public Base
{
public:
    DerivedB(const double value)
        : Base(value) {}

    [[nodiscard]] double ProcessValue() const override
    {
        return sqrt(m_Value) + m_SpecializedValueForB;
    }

private:
    double m_SpecializedValueForB = -1.0;
};

enum class [[nodiscard]] ImplementationType
{
    A = 0,
    B = 1
};

class BaseWithSwitchableImplementation
{
public:

    BaseWithSwitchableImplementation(const double value, const ImplementationType type)
    {
        InternalSetAndInitImplementationWithValue(type, value);
    }

    [[nodiscard]] double ProcessValue() const
    {
        return m_Impl->ProcessValue();
    }

    void SetAndInitImplementationWithValue(const ImplementationType type, const double value)
    {
        if (type == m_ImplType)
            return; // nothing happens

        InternalSetAndInitImplementationWithValue(type, value);
    }

private:
    void InternalSetAndInitImplementationWithValue(const ImplementationType type, const double value)
    {
        m_ImplType = type;
        m_Impl = nullptr;
        if (type == ImplementationType::A)
        {
            const auto implAPtr = std::make_shared<DerivedA>(DerivedA(value));
            m_Impl = std::static_pointer_cast<Base>(implAPtr);
        }
        else if (type == ImplementationType::B)
        {
            const auto implBPtr = std::make_shared<DerivedB>(DerivedB(value));
            m_Impl = std::static_pointer_cast<Base>(implBPtr);
        }
        else
        {
            std::cout << "ERROR: Invalid ImplementationType!\n";
        }
    }

    std::shared_ptr<Base> m_Impl;
    ImplementationType m_ImplType;
};

int main()
{
    const double value = 3.0;
    const auto instanceA = std::make_shared<DerivedA>(DerivedA(value));
    const auto instanceB = std::make_shared<DerivedB>(DerivedB(value));

    const auto basePtrA = std::static_pointer_cast<Base>(instanceA);
    const auto basePtrB = std::static_pointer_cast<Base>(instanceB);

    std::cout << "basePtrA->ProcessValue() = " << basePtrA->ProcessValue() << "\n";
    std::cout << "basePtrB->ProcessValue() = " << basePtrB->ProcessValue() << "\n";

    const auto switchableImplA = BaseWithSwitchableImplementation(value, ImplementationType::A);
    const auto switchableImplB = BaseWithSwitchableImplementation(value, ImplementationType::B);

    std::cout << "switchableImplA.ProcessValue() = " << switchableImplA.ProcessValue() << "\n";
    std::cout << "switchableImplA.ProcessValue() = " << switchableImplB.ProcessValue() << "\n";

    auto switchableImplAToB = BaseWithSwitchableImplementation(value, ImplementationType::A);

    std::cout << "switchableImplAToB.ProcessValue() = " << switchableImplAToB.ProcessValue() << "\n";

    switchableImplAToB.SetAndInitImplementationWithValue(ImplementationType::B, value);

    std::cout << "switchableImplAToB.ProcessValue() = " << switchableImplAToB.ProcessValue() << "\n";

    return 0;
}

输出:

basePtrA->ProcessValue() = 10
basePtrB->ProcessValue() = 0.732051
switchableImplA.ProcessValue() = 10
switchableImplA.ProcessValue() = 0.732051
switchableImplAToB.ProcessValue() = 10
switchableImplAToB.ProcessValue() = 0.732051

这正是我所需要的。

于 2021-11-20T19:01:12.127 回答