OOP - 存储对象和服务接口

时间:2017-05-16 08:51:39

标签: c++ oop

我目前正在从事C ++项目,我正在处理自制类 CSound 并使用 CSoundEngine 处理其实例,允许自己创建一个CSound(存储在我的CSoundEngine中)并返回指向它的指针。

事实上,出于设计目的,我希望返回一个指向接口的指针。例如,可以将真实对象存储在向量中,并通过C ++(或Java)中的接口提供它吗?

如果答案是肯定的,是否可以存储一个可以扩展的通用对象的向量,并根据子类返回一个特定的接口?

谢谢!

1 个答案:

答案 0 :(得分:0)

所以,如果我理解正确,你会有类似的东西:

class CSound {
    // Whatever
}; 

class CSoundEngine {
    // A lot of stuff
    CSound* createSound(/* ... */);
};

你不希望CSound返回,但是一个界面,虽然它内部存储在一个具有特定类型的矢量中?

第一个问题 - 界面 - 很容易解决:

创建接口并从中派生。

class ISound {
    public:
        ISound(const ISound&)             = delete;
        ISound(ISound&&)                  = delete;
        ISound& operator =(const ISound&) = delete;
        ISound& operator =(ISound&&)      = delete;

        virtual ~ISound() = default;

        // Your ISound public API here as pure virtual methods, e.g.:
        virtual const std::string name() const = 0;
    protected:
        ISound() = default;
};

class ILoopable {
    public:
        ILoopable(const ILoopable&)             = delete;
        ILoopable(ILoopable&&)                  = delete;
        ILoopable& operator =(const ILoopable&) = delete;
        ILoopable& operator =(ILoopable&&)      = delete;

        virtual ~ILoopable() = default;

        // Your ILoopable public API here as pure virtual methods, e.g.:
        virtual const bool isLoopingActive()      const = 0;
        virtual       bool setLoopingActive(bool)       = 0;
    protected:
        ILoopable() = default;
};

class CDefaultSound 
    : public ISound {
    public:
        CDefaultSound () = default;

       // ISound implementation
        inline const std::string name() const { return mName; }

    private:
        std::string mName;
};


class CLoopableSound 
    : public ISound,
      public ILoopable {
    public:
        CLoopableSound() 
            : ISound(), 
              ILoopable(),
              mLoopingActive(true),
              mName("")
        {
        }


       // ISound implementation
        inline const std::string name() const { return (mName + "(Loopable)"); }

       // ILoopable implementation
        inline const bool isLoopingActive() const { return mLoopingActive; }
        inline bool setLoopingActive(bool active) { mLoopingActive = active; }

    private:
        bool        mLoopingActive;
        std::string mName;
};

int main()
{
    // Now you can do something like this, for example, using polymorphism
    // in your CSoundEngine (see below)...
    ISound *pDef      = new CDefaultSound();
    ISound *pLoopable = new CLoopableSound();
}

如果你只想使用源自ISound的CSound,那么你不需要多个课程,但是我不明白使用界面的意义。 重要提示:由于纯虚拟接口方法,您无法实现接口类,因此您必须使用指针或RAII指针,如shared_ptr或unique_ptr(我还是推荐RAII ......)

第二个问题 - 在向量中存储特定类型 - 要困难得多,因为您需要单个向量foreach允许类型。要么!您存储接口实例并仅使用interface-methods!

class DefaultSoundCreator {
    static ISound* createSound(/* Criteria */) { ... }
};

template <typename TSoundCreator>
class CSoundEngine {
    public:
        CSoundEngine() 
            : mSoundCreator() { 
        }

        std::shared_ptr<ISound> createSound(/* some criteria */);

    private:
        std::vector<std::shared_ptr<ISound>> mSounds;
};

// cpp
std::shared_ptr<ISound> CSoundEngine::createSound(/* some criteria */) {
   // Use criteria to create specific sound classes and store them in the mSOunds vector.
   ISound *pSound = TSoundCreator::createSound(/* forward criteria for creation */);
   std::shared_ptr<ISound> pSoundPtr = std::shared_ptr<ISound>(pSound);

   mSounds.push_back(pSoundPtr);

   return pSoundPtr;
}

int main() {
   std::unique_ptr<CSoundEngine<DefaultSoundCreator>> pEngine = std::make_shared<CSoundEngine<DefaultSoundCreator>>();

   std::shared_ptr<ISound> pSound = pEngine->createSound(/* Criteria */);
}

这样你可以依赖ISound提供的功能,但是通过指定Creator类,你可以使用通用声音引擎控制声音创建。

现在使用接口基类的类型擦除的实际问题:你知道你在索引2处存储了一个CLoopableSound,但由于声音引擎的方法createSound(),你只能使用ISound-Interface方法:std :: shared_ptr的;

如何访问ILoopable-behavior?

这就是重点,它变得具有哲学性......我建议阅读:

https://akrzemi1.wordpress.com/2013/11/18/type-erasure-part-i/ Type erasure techniques https://aherrmann.github.io/programming/2014/10/19/type-erasure-with-merged-concepts/

我喜欢使用的一种技术:

 class CLoopableSound
     : public ISound {
    // All the above declarations and definitions
    // AND:
    static std::shared_ptr<CLoopableSound> fromISound(const std::shared_ptr<ISound>& other, bool *pSuccess = nullptr) {
        std::shared_ptr<CLoopableSound> p = std::static_pointer_cast<CLoopableSound>(other);
        if(pSuccess)
            *pSuccess = p.operator bool();

        return p;
    }
 };

 // Use like
 std::shared_ptr<CLoopableSound> pLoopable = CLoopableSound::fromISound(pSoundEngine->getSound(...));
 if(pLoopable) {
     // Use
 }

最后,您当然可以将fromISound-function作为模板并使用强制转换来仅访问ILoopable而不是CLoopableSound等。