দ্রষ্টব্য: নীচেরটি সি ++ 03 কোড, তবে আমরা পরের দুই বছরে সি ++ 11 এ যাওয়ার একটি প্রত্যাশা রাখি, তাই আমাদের অবশ্যই এটি মাথায় রাখতে হবে।
সি ++ এ কীভাবে বিমূর্ত ইন্টারফেসটি লিখতে হয় সে সম্পর্কে আমি একটি গাইডলাইন (অন্যদের মধ্যে, অন্যদের মধ্যে) লিখছি। আমি এই বিষয়ে সুতারের দুটি নিবন্ধ পড়েছি, উদাহরণ এবং উত্তরের জন্য ইন্টারনেট অনুসন্ধান করেছি এবং কিছু পরীক্ষা করেছি।
এই কোডটি অবশ্যই সংকলন করা উচিত নয়!
void foo(SomeInterface & a, SomeInterface & b)
{
SomeInterface c ; // must not be default-constructible
SomeInterface d(a); // must not be copy-constructible
a = b ; // must not be assignable
}
উপরের সমস্ত আচরণগুলি কাটানোর ক্ষেত্রে তাদের সমস্যার উত্সটি খুঁজে বের করে : বিমূর্ত ইন্টারফেস (বা শ্রেণিবিন্যাসে লিফ-লিফ ক্লাস) গঠনযোগ্য বা কপিযোগ্য / অ্যাসাইয়েবল হওয়া উচিত নয়, যদি উত্পন্ন শ্রেণিটি হতে পারে তবে ইভিএন।
0 তম সমাধান: বেসিক ইন্টারফেস
class VirtuallyDestructible
{
public :
virtual ~VirtuallyDestructible() {}
} ;
এই সমাধানটি সরল, এবং কিছুটা নির্বোধ: এটি আমাদের সমস্ত প্রতিবন্ধকতা ব্যর্থ করে: এটি ডিফল্ট-নির্মিত, অনুলিপি-নির্মিত এবং অনুলিপি-নির্ধারিত হতে পারে (আমি মুভ কনস্ট্রাক্টর এবং অ্যাসাইনমেন্ট সম্পর্কেও নিশ্চিত নই, তবে এখনও আমার 2 বছর সময় আছে এটি).
- আমরা ডেস্ট্রাক্টরকে খাঁটি ভার্চুয়াল ঘোষণা করতে পারি না কারণ আমাদের এটির ইনলাইন রাখা দরকার এবং আমাদের কিছু সংকলক খাঁটি ভার্চুয়াল পদ্ধতিগুলি ইনলাইন খালি শরীরের সাথে হজম করবে না।
- হ্যাঁ, এই শ্রেণীর একমাত্র পয়েন্টটি প্রয়োগকারীদের কার্যত ধ্বংসাত্মক করে তোলা, এটি বিরল ঘটনা।
- আমাদের যদি অতিরিক্ত ভার্চুয়াল খাঁটি পদ্ধতি (যা বেশিরভাগ ক্ষেত্রেই হয়) থাকে তবে এই শ্রেণিটি এখনও অনুলিপি-যোগ্য হবে।
তো, না ...
1 ম সমাধান: বুস্ট :: নন কপি
class VirtuallyDestructible : boost::noncopyable
{
public :
virtual ~VirtuallyDestructible() {}
} ;
এই সমাধানটি সেরা, কারণ এটি সরল, পরিষ্কার এবং সি ++ (কোনও ম্যাক্রো নেই) is
সমস্যাটি হ'ল এটি এখনও সেই নির্দিষ্ট ইন্টারফেসের জন্য কাজ করে না কারণ ভার্চুয়ালি কনস্ট্রাক্টেবল এখনও ডিফল্ট-নির্মিত হতে পারে ।
- আমরা ডেস্ট্রাক্টরকে খাঁটি ভার্চুয়াল ঘোষণা করতে পারি না কারণ আমাদের এটি ইনলাইন রাখতে হবে এবং আমাদের কিছু সংকলক এটি হজম করবে না।
- হ্যাঁ, এই শ্রেণীর একমাত্র পয়েন্টটি প্রয়োগকারীদের কার্যত ধ্বংসাত্মক করে তোলা, এটি বিরল ঘটনা।
আরেকটি সমস্যা হ'ল অনুলিপিযোগ্য ইন্টারফেস প্রয়োগকারী ক্লাসগুলি অবশ্যই অনুলিপি / অনুলিপি নির্ধারণ করতে হবে কপির কনস্ট্রাক্টর এবং অ্যাসাইনমেন্ট অপারেটর যদি তাদের সেই পদ্ধতিগুলির প্রয়োজন হয় (এবং আমাদের কোডে আমাদের কাছে মান ক্লাস রয়েছে যা এখনও আমাদের ক্লায়েন্টের মাধ্যমে অ্যাক্সেস করতে পারে ইন্টারফেসগুলি)।
এটি জিরো রুলের বিপরীতে যায়, যেখানে আমরা যেতে চাই: যদি ডিফল্ট বাস্তবায়ন ঠিক থাকে, তবে আমাদের এটি ব্যবহার করতে সক্ষম হওয়া উচিত।
দ্বিতীয় সমাধান: তাদের সুরক্ষিত করুন!
class MyInterface
{
public :
virtual ~MyInterface() {}
protected :
// With C++11, these methods would be "= default"
MyInterface() {}
MyInterface(const MyInterface & ) {}
MyInterface & operator = (const MyInterface & ) { return *this ; }
} ;
এই প্যাটার্নটি আমাদের ছিল এমন প্রযুক্তিগত প্রতিবন্ধকতাগুলি অনুসরণ করে (কমপক্ষে ব্যবহারকারীর কোডে): মাইআইন্টারফেসটি ডিফল্ট-নির্মাণ করা যায় না, অনুলিপি তৈরি করা যায় না এবং অনুলিপিও দেওয়া যায় না।
এছাড়াও, এটি ক্লাস বাস্তবায়নে কোনও কৃত্রিম বাধা আরোপ করে না , যা জিরো রুল অনুসরণ করতে বা এমনকি নির্ধারিত কিছু সি ++ 11/14 এ "= ডিফল্ট" হিসাবে নির্ধারিত কিছু নির্মাতাকে / অপারেটরকে মুক্ত করতে পারে।
এখন, এটি বেশ ভার্জোজ এবং একটি বিকল্পের জন্য ম্যাক্রো ব্যবহার করা হবে, এরকম কিছু:
class MyInterface
{
public :
virtual ~MyInterface() {}
protected :
DECLARE_AS_NON_SLICEABLE(MyInterface) ;
} ;
সুরক্ষিত অবশ্যই ম্যাক্রোর বাইরে থাকতে হবে (কারণ এর কোনও সুযোগ নেই)।
সঠিকভাবে "নেমস্পিড" (এটি আপনার সংস্থা বা পণ্যটির নামের সাথে উপসর্গযুক্ত), ম্যাক্রোটি নিরীহ হতে হবে।
এবং সুবিধাটি হ'ল কোডটি একটি ইন্টারফেসে বর্ণিত হয়, পরিবর্তে সমস্ত ইন্টারফেসে অনুলিপি করা হয়। ভবিষ্যতে মুভ-কনস্ট্রাক্টর এবং মুভ-অ্যাসাইনমেন্টটি স্পষ্টভাবে একইভাবে অক্ষম করা উচিত, কোডে এটি খুব হালকা পরিবর্তন হবে।
উপসংহার
- ইন্টারফেসে স্লাইসিংয়ের বিরুদ্ধে কোডটি সুরক্ষিত রাখতে আমি কী অসম্পূর্ণ? (আমি বিশ্বাস করি আমি নই, তবে কেউ কখনও জানে না ...)
- উপরেরগুলির মধ্যে সেরা সমাধানটি কী?
- এর চেয়ে ভাল সমাধান আর কি আছে?
দয়া করে মনে রাখবেন যে এটি এমন একটি প্যাটার্ন যা নবজাতকদের (অন্যদের মধ্যে) জন্য একটি গাইডলাইন হিসাবে কাজ করবে, সুতরাং একটি সমাধান যেমন: "প্রতিটি ক্ষেত্রে এর প্রয়োগ হওয়া উচিত" এটি একটি व्यवहार্য সমাধান নয়।
অনুগ্রহ এবং ফলাফল
প্রশ্নের উত্তর দেওয়ার জন্য সময় ব্যয় করা, এবং উত্তরগুলির প্রাসঙ্গিকতার কারণে আমি কর্ড্পকে অনুদান প্রদান করি ।
আমার সমস্যার সমাধানটি সম্ভবত এরকম কিছুতে যাবে:
class MyInterface
{
DECLARE_CLASS_AS_INTERFACE(MyInterface) ;
public :
// the virtual methods
} ;
... নিম্নলিখিত ম্যাক্রো সহ:
#define DECLARE_CLASS_AS_INTERFACE(ClassName) \
public : \
virtual ~ClassName() {} \
protected : \
ClassName() {} \
ClassName(const ClassName & ) {} \
ClassName & operator = (const ClassName & ) { return *this ; } \
private :
নিম্নলিখিত সমস্যার কারণে এটি আমার সমস্যার একটি কার্যকর সমাধান:
- এই শ্রেণিটি তাত্ক্ষণিকভাবে চালু করা যায় না (নির্মাণকারীরা সুরক্ষিত)
- এই শ্রেণিটি কার্যত ধ্বংস হতে পারে
- উত্তরাধিকারসূত্রে প্রাপ্ত ক্লাসগুলিতে অযৌক্তিক বাধা না চাপিয়ে এই শ্রেণিটি উত্তরাধিকার সূত্রে প্রাপ্ত হতে পারে (যেমন উত্তরাধিকার সূত্রে ডিফল্টরূপে অনুলিপিযোগ্য হতে পারে)
- ম্যাক্রোর ব্যবহারের অর্থ হল ইন্টারফেস "ঘোষণা" সহজেই সনাক্তযোগ্য (এবং সন্ধানযোগ্য), এবং এর কোডটি এক জায়গায় ফ্যাক্টর করা হয়েছে যাতে এটি পরিবর্তন করা সহজতর হয় (উপযুক্ত উপসর্গযুক্ত নামটি অনাকাঙ্ক্ষিত নাম সংঘাতগুলি সরিয়ে দেবে)
মনে রাখবেন যে অন্যান্য উত্তরগুলি মূল্যবান অন্তর্দৃষ্টি দিয়েছে। আপনাকে যারা ধন্যবাদ জানিয়েছে তাদের সবাইকে ধন্যবাদ।
মনে রাখবেন যে আমি অনুমান করি যে আমি এখনও এই প্রশ্নটিতে অন্য একটি অনুগ্রহ রাখতে পারি, এবং আমি যথেষ্ট আলোকিত উত্তরকে আমি মূল্যবান বলে মনে করি যে আমি একটি দেখতে পেলাম, আমি কেবল উত্তরটি বরাদ্দ করার জন্য একটি খোলাখুলি খুলব।
virtual ~VirtuallyDestructible() = 0
এবং ইন্টারফেস ক্লাসগুলির ভার্চুয়াল উত্তরাধিকার (কেবল বিমূর্ত সদস্যের সাথে)। আপনি সম্ভবত ভার্চুয়ালডেস্ট্রাক্টেবল বাদ দিতে পারেন।
virtual void bar() = 0;
উদাহরণ স্বরূপ? এটি আপনার ইন্টারফেসটিকে তাত্ক্ষণিকভাবে আটকাতে বাধা দেবে।