এটি কি সি ++ তে অবস্থিত "পিপল"-ভিত্তিক শ্রেণি শ্রেণিবিন্যাসের জন্য একটি ভাল পদ্ধতির?


9

আমার একটি শ্রেণিবিন্যাস রয়েছে যার জন্য আমি ইন্টারফেসটি বাস্তবায়ন থেকে আলাদা করতে চাই। আমার সমাধানটি হায়ারারচি দুটি হ'ল: ইন্টারফেসের জন্য একটি হ্যান্ডেল শ্রেণির শ্রেণিবিন্যাস এবং বাস্তবায়নের জন্য একটি জন-শ্রেণীর শ্রেণিবিন্যাস। বেস হ্যান্ডেল ক্লাসে একটি পয়েন্টার-টু-ইমপ্লিমেন্টেশন থাকে যা উত্পন্ন হ্যান্ডেল ক্লাসগুলি ডেরিভেট টাইপের পয়েন্টারে ফেলে দেয় (ফাংশন দেখুন getPimpl())।

দুটি উত্পন্ন ক্লাস সহ বেস ক্লাসের জন্য আমার সমাধানের স্কেচ এখানে। এর চেয়ে ভাল সমাধান কি আছে?

ফাইল "বেস.এইচ":

#include <memory>

class Base {
protected:
    class Impl;
    std::shared_ptr<Impl> pImpl;
    Base(Impl* pImpl) : pImpl{pImpl} {};
    ...
};

class Derived_1 final : public Base {
protected:
    class Impl;
    inline Derived_1* getPimpl() const noexcept {
        return reinterpret_cast<Impl*>(pImpl.get());
    }
public:
    Derived_1(...);
    void func_1(...) const;
    ...
};

class Derived_2 final : public Base {
protected:
    class Impl;
    inline Derived_2* getPimpl() const noexcept {
        return reinterpret_cast<Impl*>(pImpl.get());
    }
public:
    Derived_2(...);
    void func_2(...) const;
    ...
};

ফাইল "বেস.cpp":

class Base::Impl {
public:
    Impl(...) {...}
    ...
};

class Derived_1::Impl final : public Base::Impl {
public:
    Impl(...) : Base::Impl(...) {...}
    void func_1(...) {...}
    ...
};

class Derived_2::Impl final : public Base::Impl {
public:
    Impl(...) : Base::Impl(...) {...}
    void func_2(...) {...}
    ...
};

Derived_1::Derived_1(...) : Base(new Derived_1::Impl(...)) {...}
Derived_1::func_1(...) const { getPimpl()->func_1(...); }

Derived_2::Derived_2(...) : Base(new Derived_2::Impl(...)) {...}
Derived_2::func_2(...) const { getPimpl()->func_2(...); }

এই ক্লাসগুলির মধ্যে কোনটি লাইব্রেরি / উপাদানগুলির বাইরে থেকে দৃশ্যমান হবে? যদি কেবলমাত্র Base, একটি সাধারণ বিমূর্ত বেস ক্লাস ("ইন্টারফেস") এবং পিম্পল ছাড়াই কংক্রিট বাস্তবায়ন যথেষ্ট হতে পারে।
ডি জুরকাউ

@ ডি জুরকাউ বেস এবং উত্পন্ন ক্লাসগুলি সমস্ত প্রকাশ্যে দৃশ্যমান হবে। স্পষ্টতই, বাস্তবায়ন ক্লাসগুলি তা করবে না।
স্টিভ এমারসন

কেন হতাশ? বেস ক্লাসটি এখানে অদ্ভুত অবস্থানে রয়েছে, এটি একটি ভাগ করা পয়েন্টার দিয়ে উন্নত টাইপস্যাফটি এবং কম কোড সহ প্রতিস্থাপন করা যেতে পারে।
বেসিলিভ

@ বাসিলিভগুলি আমি বুঝতে পারি না। পাবলিক বেস ক্লাস বাস্তবায়নটি আড়াল করতে পিম্পল আইডিয়ম ব্যবহার করে। আমি দেখছি না কীভাবে এটি ভাগ করে নেওয়া পয়েন্টারের সাথে প্রতিস্থাপন পয়েন্টারটি ingালাই বা ডুপ্লিকেট না করে শ্রেণি শ্রেণিবিন্যাসকে বজায় রাখতে পারে। আপনি একটি কোড উদাহরণ প্রদান করতে পারেন?
স্টিভ এমারসন

আমি ডাউনকাস্টের অনুলিপি না করে পয়েন্টারটির সদৃশ করার প্রস্তাব দিই।
বেসিলিভ

উত্তর:


1

আমি মনে করি এটি Derived_1::Implথেকে প্রাপ্ত কৌশলগুলি একটি দুর্বল Base::Impl

পিম্পল আইডিয়ামটি ব্যবহারের মূল উদ্দেশ্যটি কোনও শ্রেণীর প্রয়োগের বিবরণ গোপন করা। এ Derived_1::Implথেকে উদ্ভূত হতে দিয়ে Base::Impl, আপনি সেই উদ্দেশ্যকে পরাজিত করেছেন। এখন, কেবল বাস্তবায়ন Baseনির্ভর করে না Base::Impl, বাস্তবায়নও Derived_1নির্ভর করে Base::Impl

এর চেয়ে ভাল সমাধান কি আছে?

এটি নির্ভর করে কি বাণিজ্য-অফ আপনার পক্ষে গ্রহণযোগ্য।

সমাধান ঘ

করুন Implশ্রেণীর সম্পূর্ণ স্বতন্ত্র। এটি সূচিত করবে যে Implক্লাসে দুটি পয়েন্টার থাকবে - একটিতে Baseএবং অন্যটিতে Derived_N

class Base {

   protected:
      Base() : pImpl{new Impl()} {}

   private:
      // It's own Impl class and pointer.
      class Impl { };
      std::shared_ptr<Impl> pImpl;

};

class Derived_1 final : public Base {
   public:
      Derived_1() : Base(), pImpl{new Impl()} {}
      void func_1() const;
   private:
      // It's own Impl class and pointer.
      class Impl { };
      std::shared_ptr<Impl> pImpl;
};

সমাধান 2

ক্লাসগুলি কেবল হ্যান্ডল হিসাবে প্রকাশ করুন। শ্রেণীর সংজ্ঞা এবং বাস্তবায়ন মোটেই প্রকাশ করবেন না।

সর্বজনীন শিরোলেখ ফাইল:

struct Handle {unsigned long id;};
struct Derived1_tag {};
struct Derived2_tag {};

Handle constructObject(Derived1_tag tag);
Handle constructObject(Derived2_tag tag);

void deleteObject(Handle h);

void fun(Handle h, Derived1_tag tag);
void bar(Handle h, Derived2_tag tag); 

এখানে দ্রুত বাস্তবায়ন

#include <map>

class Base
{
   public:
      virtual ~Base() {}
};

class Derived1 : public Base
{
};

class Derived2 : public Base
{
};

namespace Base_Impl
{
   struct CompareHandle
   {
      bool operator()(Handle h1, Handle h2) const
      {
         return (h1.id < h2.id);
      }
   };

   using ObjectMap = std::map<Handle, Base*, CompareHandle>;

   ObjectMap& getObjectMap()
   {
      static ObjectMap theMap;
      return theMap;
   }

   unsigned long getNextID()
   {
      static unsigned id = 0;
      return ++id;
   }

   Handle getHandle(Base* obj)
   {
      auto id = getNextID();
      Handle h{id};
      getObjectMap()[h] = obj;
      return h;
   }

   Base* getObject(Handle h)
   {
      return getObjectMap()[h];
   }

   template <typename Der>
      Der* getObject(Handle h)
      {
         return dynamic_cast<Der*>(getObject(h));
      }
};

using namespace Base_Impl;

Handle constructObject(Derived1_tag tag)
{
   // Construct an object of type Derived1
   Derived1* obj = new Derived1;

   // Get a handle to the object and return it.
   return getHandle(obj);
}

Handle constructObject(Derived2_tag tag)
{
   // Construct an object of type Derived2
   Derived2* obj = new Derived2;

   // Get a handle to the object and return it.
   return getHandle(obj);
}

void deleteObject(Handle h)
{
   // Get a pointer to Base given the Handle.
   //
   Base* obj = getObject(h);

   // Remove it from the map.
   // Delete the object.
   if ( obj != nullptr )
   {
      getObjectMap().erase(h);
      delete obj;
   }
}

void fun(Handle h, Derived1_tag tag)
{
   // Get a pointer to Derived1 given the Handle.
   Derived1* obj = getObject<Derived1>(h);
   if ( obj == nullptr )
   {
      // Problem.
      // Decide how to deal with it.

      return;
   }

   // Use obj
}

void bar(Handle h, Derived2_tag tag)
{
   Derived2* obj = getObject<Derived2>(h);
   if ( obj == nullptr )
   {
      // Problem.
      // Decide how to deal with it.

      return;
   }

   // Use obj
}

সুবিধা - অসুবিধা

প্রথম পদ্ধতির সাথে, আপনি Derivedস্ট্যাকে ক্লাস তৈরি করতে পারেন । দ্বিতীয় পদ্ধতির সাথে, এটি কোনও বিকল্প নয়।

প্রথম পদ্ধতির সাথে, আপনি Derivedস্ট্যাকের মধ্যে একটি নির্মাণ এবং ধ্বংস করার জন্য দুটি গতিশীল বরাদ্দ এবং ডিওলোকেশনগুলির জন্য ব্যয় করতে পারেন । আপনি যদি Derivedগাদা থেকে কোনও অবজেক্ট তৈরি এবং ধ্বংস করে দেন তবে আরও একটি বরাদ্দ এবং অবনতির জন্য ব্যয় করতে হবে। দ্বিতীয় পদ্ধতির সাহায্যে, আপনি কেবলমাত্র প্রতিটি বস্তুর জন্য একটি গতিশীল বরাদ্দ এবং এক ডিওলোকেশন ব্যয় করতে পারেন।

প্রথম পদ্ধতির সাথে, আপনি virtualসদস্য ফাংশনটি ব্যবহার করার ক্ষমতা পাবেন Base। দ্বিতীয় পদ্ধতির সাথে, এটি কোনও বিকল্প নয়।

আমার পরামর্শ

আমি প্রথম সমাধানটি নিয়ে যাব যাতে আমি ক্লাস হায়ারার্কি এবং virtualসদস্য ফাংশনগুলি Baseকিছুটা ব্যয়বহুল হলেও ব্যবহার করতে পারি ।


0

আমি এখানে কেবলমাত্র উন্নতি দেখতে পাচ্ছি কংক্রিটের ক্লাসগুলি বাস্তবায়ন ক্ষেত্রটি সংজ্ঞায়িত করা। যদি বিমূর্ত বেস শ্রেণীর এটির প্রয়োজন হয়, তারা একটি বিমূর্ত সম্পত্তি সংজ্ঞায়িত করতে পারেন যা কংক্রিট শ্রেণিতে কার্যকর করা সহজ:

Base.h

class Base {
protected:
    class Impl;
    virtual std::shared_ptr<Impl> getImpl() =0;
    ...
};

class Derived_1 final : public Base {
protected:
    class Impl1;
    std::shared_ptr<Impl1> pImpl
    virtual std::shared_ptr<Base::Impl> getImpl();
public:
    Derived_1(...);
    void func_1(...) const;
    ...
};

Base.cpp

class Base::Impl {
public:
    Impl(...) {...}
    ...
};

class Derived_1::Impl1 final : public Base::Impl {
public:
    Impl(...) : Base::Impl(...) {...}
    void func_1(...) {...}
    ...
};

std::shared_ptr<Base::Impl> Derived_1::getImpl() { return pPimpl; }
Derived_1::Derived_1(...) : pPimpl(std::make_shared<Impl1>(...)) {...}
void Derived_1::func_1(...) const { pPimpl->func_1(...); }

এটি আমার কাছে নিরাপদ বলে মনে হচ্ছে। আপনার যদি বড় গাছ থাকে তবে আপনি গাছের virtual std::shared_ptr<Impl1> getImpl1() =0মাঝখানেও পরিচয় করিয়ে দিতে পারেন ।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.