আপনি কীভাবে সি ++ তে একটি ইন্টারফেস ঘোষণা করবেন?


উত্তর:


685

Bradtgmurray দ্বারা উত্তরের প্রসারিত করতে , আপনি ভার্চুয়াল ডেস্ট্রাক্টর যুক্ত করে আপনার ইন্টারফেসের খাঁটি ভার্চুয়াল পদ্ধতি তালিকায় একটি ব্যতিক্রম করতে চাইতে পারেন। এটি আপনাকে কংক্রিটের উত্পন্ন ক্লাসটি প্রকাশ না করেই অন্য দলের কাছে পয়েন্টার মালিকানা পাস করতে দেয়। ডেস্ট্রাক্টরকে কিছু করতে হবে না, কারণ ইন্টারফেসের কোনও কংক্রিট সদস্য নেই। কোনও ফাংশনটিকে ভার্চুয়াল এবং ইনলাইন উভয় হিসাবে সংজ্ঞায়িত করার জন্য এটি পরস্পরবিরোধী বলে মনে হতে পারে তবে আমার বিশ্বাস করুন - এটি নয়।

class IDemo
{
    public:
        virtual ~IDemo() {}
        virtual void OverrideMe() = 0;
};

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

class Child : public Parent, public IDemo
{
    public:
        virtual void OverrideMe()
        {
            //do stuff
        }
};

ভার্চুয়াল ডেস্ট্রাক্টরের জন্য আপনাকে কোনও দেহ অন্তর্ভুক্ত করতে হবে না - এটি বেরিয়ে আসে কিছু সংকলককে খালি ডেস্ট্রাক্টরকে অনুকূল করতে সমস্যা হয় এবং আপনি ডিফল্টটি ব্যবহার করে আরও ভাল হন।


105
ভার্চুয়াল বর্ণনকারী ++! এই অত্যন্ত গুরুত্বপূর্ণ. আপনি অপারেটারের খাঁটি ভার্চুয়াল ঘোষণাগুলি = এবং অনুলিপি নির্মাণকারীর সংজ্ঞাগুলিও অন্তর্ভুক্ত করতে চাইতে পারেন যা আপনার জন্য সংকলকটি স্বয়ংক্রিয়ভাবে উত্পন্ন করছে prevent
xan

33
ভার্চুয়াল ডেস্ট্রাক্টরের বিকল্প হ'ল সুরক্ষিত ডেস্ট্রাক্টর। এটি পলিমারফিক ধ্বংসকে অক্ষম করে, যা কিছু পরিস্থিতিতে আরও উপযুক্ত হতে পারে। Getw.ca/publications/mill18.htm এ "গাইডলাইন # 4" সন্ধান করুন
ফ্রেড লারসন

9
আরেকটি বিকল্প হ'ল =0দেহ সহ খাঁটি ভার্চুয়াল ( ) ডেস্ট্রাক্টরকে সংজ্ঞায়িত করা । এখানে সুবিধাটি হ'ল সংকলকটি তাত্ত্বিকভাবে দেখতে পারেন যে এখন ভেটেবলের কোনও বৈধ সদস্য নেই, এবং এটি পুরোপুরি ফেলে দিতে পারেন। একটি বডি সহ ভার্চুয়াল ডেস্ট্রাক্টরের সাথে, বলে যে বিন্যাসকারীকে (ভার্চুয়াল) বলা যেতে পারে যেমন thisপয়েন্টারের মাধ্যমে নির্মাণের মাঝখানে (যখন নির্মিত বস্তুটি এখনও Parentটাইপ থাকে), এবং তাই সংকলককে একটি বৈধ vtable সরবরাহ করতে হবে। সুতরাং আপনি যদি thisনির্মাণের সময় স্পষ্টভাবে ভার্চুয়াল ডেস্ট্রাক্টরদের কল না করেন :) আপনি কোড আকারে সঞ্চয় করতে পারবেন।
পাভেল মিনায়েভ

50
সি ++ উত্তরের কতটা সাধারণ বৈশিষ্ট্য যে শীর্ষ উত্তরটি সরাসরি প্রশ্নের উত্তর দেয় না (যদিও স্পষ্টতই কোডটি নিখুঁত) তবে এর পরিবর্তে এটি সহজ উত্তরটিকে অনুকূল করে দেয়।
টিম

18
ভুলে যাবেন না যে সি ++ 11 এ আপনি overrideসংকলন-সময় আর্গুমেন্ট এবং রিটার্ন মান ধরণের পরীক্ষার জন্য কীওয়ার্ডটি নির্দিষ্ট করতে পারেন । উদাহরণস্বরূপ, সন্তানের ঘোষণায়virtual void OverrideMe() override;
শান

243

খাঁটি ভার্চুয়াল পদ্ধতিতে একটি শ্রেণি তৈরি করুন। অন্য শ্রেণি তৈরি করে ইন্টারফেসটি ব্যবহার করুন যা সেই ভার্চুয়াল পদ্ধতিগুলিকে ওভাররাইড করে।

খাঁটি ভার্চুয়াল পদ্ধতিটি একটি শ্রেণি পদ্ধতি যা ভার্চুয়াল হিসাবে সংজ্ঞায়িত হয় এবং 0 তে নির্ধারিত হয়।

class IDemo
{
    public:
        virtual ~IDemo() {}
        virtual void OverrideMe() = 0;
};

class Child : public IDemo
{
    public:
        virtual void OverrideMe()
        {
            //do stuff
        }
};

29
আইডেমোতে আপনার কোনও কিছুই করতে হবে না যাতে এটি করণীয় হিসাবে আচরণের সংজ্ঞা দেওয়া হয়: আইডেমো * পি = নতুন শিশু; / * যাই হোক * / ডিলিট পি;
ইভান তেরান

11
চাইল্ড ক্লাসে ওভাররাইডমি পদ্ধতি ভার্চুয়াল কেন? এটা কি প্রয়োজনীয়?
সেমরে

9
@ কেমরে - না এটি প্রয়োজনীয় নয় তবে এটির কোনও ক্ষতি হয় না।
PowerApp101

11
'ভার্চুয়াল' কীওয়ার্ডটি ভার্চুয়াল পদ্ধতিতে যখনই ওভাররাইড করা হয় তবে এটি সাধারণত একটি ভাল ধারণা। যদিও প্রয়োজন নেই, এটি কোডটিকে আরও পরিষ্কার করে তুলতে পারে - অন্যথায়, আপনার কোনও ইঙ্গিত নেই যে এই পদ্ধতিটি বহুগুণে ব্যবহার করা যেতে পারে, বা এমনকি বেস শ্রেণিতে বিদ্যমান exists
কেভিন 20

27
সাধারণ আলোচনা ছাড়া @Kevin overrideC ++ 11
Keyser

146

সি # / জাভাতে বিমূর্ত বেস ক্লাস ছাড়াও আপনার একটি বিশেষ ইন্টারফেস টাইপ-বিভাগের পুরো কারণ হ'ল সি # / জাভা একাধিক উত্তরাধিকার সমর্থন করে না।

সি ++ একাধিক উত্তরাধিকারকে সমর্থন করে এবং তাই একটি বিশেষ ধরণের প্রয়োজন হয় না। নন-অ্যাবস্ট্রাক্ট (খাঁটি ভার্চুয়াল) পদ্ধতিবিহীন একটি বিমূর্ত বেস শ্রেণিটি কার্যত সি # / জাভা ইন্টারফেসের সমতুল্য।


17
আমাদের এত বেশি টাইপ করা থেকে ভার্চুয়াল, = 0, ভার্চুয়াল ডেস্ট্রাক্টর বাঁচাতে ইন্টারফেস তৈরি করতে সক্ষম হতে পেরে ভাল লাগবে। এছাড়াও একাধিক উত্তরাধিকার আমার কাছে সত্যই খারাপ ধারণা বলে মনে হয় এবং আমি এটি ব্যবহারে কখনও দেখিনি, তবে ইন্টারফেসের প্রয়োজন সর্বদা। খারাপ কারণ সি ++ কমিট ইন্টারফেসগুলি প্রবর্তন করবে না কারণ আমি সেগুলি চাই।
11

9
হ 11 ম: এটির ইন্টারফেস রয়েছে। তাদের খাঁটি ভার্চুয়াল পদ্ধতি সহ কোনও ক্লাস বলা হয় এবং কোনও পদ্ধতি বাস্তবায়ন হয় না।
মাইলস রুট

6
@ ডক: জাভা.লাং.থ্রেডের এমন পদ্ধতি এবং ধ্রুবক রয়েছে যা আপনি সম্ভবত আপনার অবজেক্টে রাখতে চান না। আপনি থ্রেড থেকে প্রসারিত এবং জনসাধারণের পদ্ধতিতে চেকঅ্যাক্সেস () সহ অন্য একটি শ্রেণীর কি সংকলকটি করা উচিত? আপনি কি সি ++ এর মতো দৃ strongly়ভাবে নামযুক্ত বেস পয়েন্টারগুলি ব্যবহার করতে পছন্দ করবেন? এটি খারাপ ডিজাইনের মতো মনে হচ্ছে, আপনার সাধারণত এমন রচনা প্রয়োজন যেখানে আপনি মনে করেন যে আপনার একাধিক উত্তরাধিকার প্রয়োজন।
Ha11owed

4
@ হ্যাড 11 এগুলি অনেক আগেই ছিল তাই আমি বিশদটি মনে করি না তবে এতে আমার ক্লাসে থাকতে আগ্রহী এমন পদ্ধতি এবং বিষয়গুলি ছিল এবং আরও গুরুত্বপূর্ণভাবে আমি চেয়েছিলাম যে আমার উদ্ভূত শ্রেণীর বস্তুটি একটি Threadউদাহরণ হয়ে উঠুক । একাধিক উত্তরাধিকার খারাপ নকশা পাশাপাশি রচনা হতে পারে। এটি সব ক্ষেত্রে নির্ভর করে।
ডক

2
@ ডেভ: সত্যি? উদ্দেশ্য-সি সংকলন-সময় মূল্যায়ন, এবং টেম্পলেট আছে?
উত্সাহীকারী

51

সি ++ তে প্রতি সেফের "ইন্টারফেস" ধারণা নেই। আফাইক, একাধিক উত্তরাধিকারের অভাবে কাজ করার জন্য প্রথমে জাভাতে ইন্টারফেস চালু করা হয়েছিল। এই ধারণাটি বেশ কার্যকর হিসাবে দেখা গেছে, এবং একই প্রভাবটি একটি বিমূর্ত বেস বর্গ ব্যবহার করে সি ++ এ অর্জন করা যেতে পারে।

অ্যাবস্ট্রাক্ট বেস ক্লাসটি এমন একটি ক্লাস যেখানে কমপক্ষে একটি সদস্য ফাংশন (জাভা লিঙ্গোর পদ্ধতি) নীচের সিনট্যাক্স ব্যবহার করে ঘোষিত একটি খাঁটি ভার্চুয়াল ফাংশন:

class A
{
  virtual void foo() = 0;
};

একটি বিমূর্ত বেস শ্রেণি তাত্ক্ষণিকভাবে চালু করা যায় না, অর্থাত আপনি ক্লাস এ এর ​​কোনও বিষয় ঘোষণা করতে পারবেন না আপনি কেবল এ থেকে ক্লাস অর্জন করতে পারেন, তবে যে কোনও উদ্ভূত শ্রেণি যা বাস্তবায়ন প্রদান করে না foo()তাও বিমূর্ত হবে। বিমূর্ত হওয়া বন্ধ করার জন্য, একটি উত্পন্ন শ্রেণিকে অবশ্যই উত্তরাধিকার সূত্রে প্রাপ্ত সমস্ত খাঁটি ভার্চুয়াল ফাংশনগুলির জন্য বাস্তবায়ন সরবরাহ করতে হবে।

মনে রাখবেন যে একটি বিমূর্ত বেস শ্রেণি ইন্টারফেসের চেয়ে বেশি হতে পারে, কারণ এতে ডেটা সদস্য এবং সদস্য ফাংশন থাকতে পারে যা খাঁটি ভার্চুয়াল নয়। একটি ইন্টারফেসের সমতুল্য হ'ল কেবল বিশুদ্ধ ভার্চুয়াল ফাংশন সহ কোনও ডেটা ছাড়াই একটি বিমূর্ত বেস শ্রেণি হবে।

এবং, মার্ক র্যানসোম ইঙ্গিত অনুসারে, একটি বিমূর্ত বেস শ্রেণীর ক্ষেত্রে কোনও বেস ক্লাসের মতো ভার্চুয়াল ডেস্ট্রাক্টর সরবরাহ করা উচিত।


13
"একাধিক উত্তরাধিকারের অভাব" এর চেয়ে বেশি আমি বলব, একাধিক উত্তরাধিকার প্রতিস্থাপন করতে। জাভা শুরু থেকেই এইভাবে ডিজাইন করা হয়েছিল কারণ একাধিক উত্তরাধিকার যা সমাধান করে তার চেয়ে বেশি সমস্যা তৈরি করে। ভাল উত্তর
অস্কারলাইজ

11
অস্কার, এটি আপনি সি ++ প্রোগ্রামার যা জাভা শিখেছিলেন বা বিপরীতে। :) আইএমএইচও, যদি সি ++ এর প্রায় কোনও কিছুর মতো ন্যায়বিচারের সাথে ব্যবহার করা হয় তবে একাধিক উত্তরাধিকার সমস্যা সমাধান করে। একটি "ইন্টারফেস" বিমূর্ত বেস শ্রেণি একাধিক উত্তরাধিকারের খুব ন্যায়বিচারের ব্যবহারের উদাহরণ।
ডিমা

8
@ অস্কার রাইজ ভুল এমআই শুধুমাত্র অপব্যবহারের সময় সমস্যা তৈরি করে। এমআই-র বেশিরভাগ কথিত সমস্যাগুলি বিকল্প নকশাগুলি (এমআই ছাড়াই) নিয়ে আসে। লোকেরা যখন তাদের ডিজাইনে এমআই এর সাথে সমস্যা হয়, তখন এটি এমআই এর দোষ; যদি তাদের এসআই-তে কোনও ডিজাইনের সমস্যা হয় তবে এটি তাদের নিজস্ব ত্রুটি। "মৃত্যুর হীরা" (বারবার উত্তরাধিকার) একটি প্রধান উদাহরণ। এমআই বাশিং খাঁটি ভণ্ডামি নয়, তবে কাছাকাছি।
কৌতূহলী

4
শব্দার্থকভাবে, ইন্টারফেসগুলি বিমূর্ত শ্রেণীর চেয়ে পৃথক, তাই জাভার ইন্টারফেসগুলি কেবল প্রযুক্তিগত কাজ নয়। একটি ইন্টারফেস বা বিমূর্ত শ্রেণীর সংজ্ঞা দেওয়ার মধ্যে পছন্দটি প্রযুক্তিগত বিবেচনায় নয়, শব্দার্থ দ্বারা চালিত। আসুন আমরা কিছু ইন্টারফেস "হাসেনজাইন" কল্পনা করি: এটি একটি দিক, একটি বৈশিষ্ট্য এবং এটি প্রয়োগ করা / প্রয়োগ করা যেতে পারে খুব বিভিন্ন ধরণের (শ্রেণি বা বিমূর্ত শ্রেণি কিনা), সুতরাং আমরা তার জন্য একটি ইন্টারফেস সংজ্ঞায়িত করব, একটি বিমূর্ত শ্রেণি নয় class
মারেক স্ট্যানলি

2
@ মেরেকস্ট্যানলি, আপনি ঠিক থাকতে পারেন, তবে আমি আশা করি আপনি আরও ভাল উদাহরণ বেছে নিতে চান। আমি একটি ইন্টারফেস বংশোদ্ভূত বনাম একটি বাস্তবায়নের উত্তরাধিকার হিসাবে বিবেচনা করতে চাই। সি ++ এ আপনি উভয়ই ইন্টারফেস এবং বাস্তবায়ন একসাথে (জনসাধারণের উত্তরাধিকার) এর উত্তরাধিকারী হতে পারেন বা আপনি কেবল বাস্তবায়ন (ব্যক্তিগত উত্তরাধিকার) এর উত্তরাধিকারী হতে পারেন। জাভাতে আপনার কাছে প্রয়োগ ছাড়া কোনও ইন্টারফেস উত্তরাধিকার সূত্রে প্রাপ্ত বিকল্প রয়েছে।
দিমা

43

আমি যতদূর পরীক্ষা করতে পারি, ভার্চুয়াল ডেস্ট্রাক্টর যুক্ত করা খুব গুরুত্বপূর্ণ। আমি এর সাথে নির্মিত newএবং ধ্বংস হওয়া বস্তুগুলি ব্যবহার করছি delete

আপনি যদি ইন্টারফেসে ভার্চুয়াল ডেস্ট্রাক্টর যোগ না করেন তবে উত্তরাধিকারসূত্রে প্রাপ্ত শ্রেণীর ডেস্ট্রাক্টরকে ডাকা হয় না।

class IBase {
public:
    virtual ~IBase() {}; // destructor, use it to call destructor of the inherit classes
    virtual void Describe() = 0; // pure virtual method
};

class Tester : public IBase {
public:
    Tester(std::string name);
    virtual ~Tester();
    virtual void Describe();
private:
    std::string privatename;
};

Tester::Tester(std::string name) {
    std::cout << "Tester constructor" << std::endl;
    this->privatename = name;
}

Tester::~Tester() {
    std::cout << "Tester destructor" << std::endl;
}

void Tester::Describe() {
    std::cout << "I'm Tester [" << this->privatename << "]" << std::endl;
}


void descriptor(IBase * obj) {
    obj->Describe();
}

int main(int argc, char** argv) {

    std::cout << std::endl << "Tester Testing..." << std::endl;
    Tester * obj1 = new Tester("Declared with Tester");
    descriptor(obj1);
    delete obj1;

    std::cout << std::endl << "IBase Testing..." << std::endl;
    IBase * obj2 = new Tester("Declared with IBase");
    descriptor(obj2);
    delete obj2;

    // this is a bad usage of the object since it is created with "new" but there are no "delete"
    std::cout << std::endl << "Tester not defined..." << std::endl;
    descriptor(new Tester("Not defined"));


    return 0;
}

আপনি যদি পূর্ববর্তী কোডটি চালনা করে চালিয়ে যান তবে আপনি virtual ~IBase() {};দেখতে পাবেন যে বিনষ্টকারীকে Tester::~Tester()কখনই ডাকা হয় না।


3
ব্যবহারিক, সংকলনযোগ্য উদাহরণ সরবরাহ করে বিষয়টিকে সঠিক রূপ দেওয়ার কারণে এই পৃষ্ঠায় সেরা উত্তর। চিয়ার্স!
লুমি

1
পরীক্ষক :: es পরীক্ষক () তখনই চালিত হয় যখন আপত্তিটি "পরীক্ষকের সাথে ঘোষণা করা হয়"।
আলেসান্দ্রো এল।

প্রকৃতপক্ষে, স্ট্রিং প্রাইভেটনামের ডেস্ট্রাক্টরকে ডাকা হবে এবং মেমরির ক্ষেত্রেই এটি বরাদ্দ করা হবে। যতক্ষণ রানটাইম সম্পর্কিত, কোনও শ্রেণীর সমস্ত কংক্রিট সদস্য যখন ধ্বংস হয়, ক্লাসের উদাহরণটি। আমি একটি লাইন ক্লাসের সাথে একই ধরণের পরীক্ষার চেষ্টা করেছি যার দুটি পয়েন্ট স্ট্রাক্ট ছিল এবং আমি দেখতে পেলাম যে একটি মুছুন কল বা ঘেরাও ফাংশন থেকে ফিরে আসার জন্য উভয় স্ট্রাস্ট বিনষ্ট হয়েছে (হা!)। valgrind 0 ফাঁস নিশ্চিত করেছে
ক্রিস রেড

33

আমার উত্তরটি মূলত অন্যদের মতই তবে আমার মনে হয় আরও দুটি গুরুত্বপূর্ণ জিনিস করা আছে:

  1. আপনার ইন্টারফেসে ভার্চুয়াল ডেস্ট্রাক্টর ঘোষণা করুন বা যদি কেউ কোনও ধরণের অবজেক্ট মুছতে চেষ্টা করে তবে অপরিজ্ঞাত আচরণগুলি এড়াতে একটি সুরক্ষিত অ-ভার্চুয়ালকে তৈরি করুন IDemo

  2. একাধিক উত্তরাধিকার নিয়ে সমস্যা এড়াতে ভার্চুয়াল উত্তরাধিকার ব্যবহার করুন। (যখন আমরা ইন্টারফেসগুলি ব্যবহার করি তখন প্রায়শই একাধিক উত্তরাধিকার থাকে))

এবং অন্যান্য উত্তরের মতো:

  • খাঁটি ভার্চুয়াল পদ্ধতিতে একটি শ্রেণি তৈরি করুন।
  • অন্য শ্রেণি তৈরি করে ইন্টারফেসটি ব্যবহার করুন যা সেই ভার্চুয়াল পদ্ধতিগুলিকে ওভাররাইড করে।

    class IDemo
    {
        public:
            virtual void OverrideMe() = 0;
            virtual ~IDemo() {}
    }

    অথবা

    class IDemo
    {
        public:
            virtual void OverrideMe() = 0;
        protected:
            ~IDemo() {}
    }

    এবং

    class Child : virtual public IDemo
    {
        public:
            virtual void OverrideMe()
            {
                //do stuff
            }
    }

2
কোনও ইন্টারফেসে আপনার কোনও ডেটা সদস্য না থাকায় ভার্চুয়াল উত্তরাধিকারের প্রয়োজন নেই।
রোবোকাইড

3
ভার্চুয়াল উত্তরাধিকার পদ্ধতিগুলির জন্যও গুরুত্বপূর্ণ। এটি ব্যতীত আপনি ওভাররাইডমি () এর সাথে অস্পষ্টতার মধ্যে চলে যাবেন, এমনকি এর 'উদাহরণগুলির মধ্যে একটি' খাঁটি ভার্চুয়াল হলেও (কেবল এটি নিজে চেষ্টা করে দেখেছি)।
নর্ফ নাভিলাস

5
@ আভিশায়_ " ইন্টারফেসে আপনার কোনও ডেটা সদস্য না থাকায় ভার্চুয়াল উত্তরাধিকারের প্রয়োজন নেই। " ভুল r
কৌতূহলী

লক্ষ্য করুন যে ভার্চুয়াল উত্তরাধিকারটি কিছু জিসিসি সংস্করণে কাজ করতে পারে না, সংস্করণ 4.3.3 হিসাবে যা WinAVR 2010 এর সাথে প্রেরণ করা হয়েছে: gcc.gnu.org/bugzilla/show_bug.cgi?id=35067
mMontu

একটি ভার্চুয়াল সুরক্ষিত ডেস্ট্রাক্টর থাকার জন্য -1, দুঃখিত
ওল্ফ

10

সি ++ 11 এ আপনি সহজেই উত্তরাধিকার সম্পূর্ণ এড়াতে পারবেন:

struct Interface {
  explicit Interface(SomeType& other)
  : foo([=](){ return other.my_foo(); }), 
    bar([=](){ return other.my_bar(); }), /*...*/ {}
  explicit Interface(SomeOtherType& other)
  : foo([=](){ return other.some_foo(); }), 
    bar([=](){ return other.some_bar(); }), /*...*/ {}
  // you can add more types here...

  // or use a generic constructor:
  template<class T>
  explicit Interface(T& other)
  : foo([=](){ return other.foo(); }), 
    bar([=](){ return other.bar(); }), /*...*/ {}

  const std::function<void(std::string)> foo;
  const std::function<void(std::string)> bar;
  // ...
};

এই ক্ষেত্রে, একটি ইন্টারফেসের রেফারেন্স সিমানটিকস রয়েছে, অর্থাত্ আপনাকে নিশ্চিত করতে হবে যে অবজেক্টটি ইন্টারফেসটিকে আউটলাইভ করে (এটি মূল্য শব্দার্থক দ্বারা ইন্টারফেস করাও সম্ভব)।

এই ধরণের ইন্টারফেসগুলির উপকারিতা এবং বিধিগুলি রয়েছে:

  • উত্তরাধিকার ভিত্তিক পলিমারফিজমের চেয়ে তাদের আরও মেমরির প্রয়োজন
  • তারা উত্তরাধিকার ভিত্তিক পলিমারফিজমের চেয়ে সাধারণভাবে দ্রুত
  • যে ক্ষেত্রে আপনি চূড়ান্ত প্রকারটি জানেন, সেগুলিতে তারা আরও দ্রুত! (কিছু সংকলক যেমন জিসিসি এবং ক্ল্যাংগুলি ভার্চুয়াল ফাংশনগুলির সাথে টাইপগুলি থেকে উত্তরাধিকারসূত্রে পাওয়া যায় না এমন ধরণের ক্ষেত্রে আরও বেশি অনুকূলিতকরণ করে)।

অবশেষে, জটিল সফ্টওয়্যার ডিজাইনের সমস্ত উত্তরণের মূল হ'ল উত্তরাধিকার। ইন শন পিতামাতাদের মূল্য শব্দ এবং ধারণা ভিত্তিক পলিমরফিজ্ম (অত্যন্ত বাঞ্ছনীয় এই প্রযুক্তিটি ভালো সংস্করণ আছে ব্যাখ্যা করা হয়) নিম্নলিখিত ক্ষেত্রে চর্চিত হয়:

বলুন আমার একটি অ্যাপ্লিকেশন রয়েছে যাতে আমি MyShapeইন্টারফেসটি ব্যবহার করে বহু আকারে আমার আকারগুলি ব্যবহার করি :

struct MyShape { virtual void my_draw() = 0; };
struct Circle : MyShape { void my_draw() { /* ... */ } };
// more shapes: e.g. triangle

আপনার অ্যাপ্লিকেশনটিতে, আপনি YourShapeইন্টারফেসটি ব্যবহার করে বিভিন্ন আকারের সাথে একই কাজটি করেন :

struct YourShape { virtual void your_draw() = 0; };
struct Square : YourShape { void your_draw() { /* ... */ } };
/// some more shapes here...

এখন বলুন যে আমি আপনার অ্যাপ্লিকেশনটিতে বিকাশ করেছি এমন কিছু আকার ব্যবহার করতে চাই। ধারণামূলকভাবে, আমাদের আকারগুলির একই ইন্টারফেস রয়েছে, তবে আমার আকারগুলি আপনার প্রয়োগে কাজ করতে আপনাকে আমার আকারগুলি নিম্নরূপে প্রসারিত করতে হবে:

struct Circle : MyShape, YourShape { 
  void my_draw() { /*stays the same*/ };
  void your_draw() { my_draw(); }
};

প্রথমত, আমার আকারগুলি সংশোধন করা মোটেও সম্ভব নয়। তদ্ব্যতীত, একাধিক উত্তরাধিকার স্প্যাগেটি কোডের পথে নিয়ে যায় (কল্পনা করুন যে কোনও তৃতীয় প্রকল্প এসেছে যার মধ্যে TheirShapeইন্টারফেসটি ব্যবহার করা হচ্ছে ... তারা যদি তাদের ড্র ফাংশনও বলে তবে কী ঘটে my_draw?)

আপডেট: অ-উত্তরাধিকার ভিত্তিক পলিমারফিজম সম্পর্কে কয়েকটি নতুন উল্লেখ রয়েছে:


5
টিবিএইচ উত্তরাধিকার সেই সি ++ 11 টির চেয়ে অনেক বেশি স্পষ্ট, যা একটি ইন্টারফেস বলে ভান করে, তবে কিছু অসামঞ্জস্য নকশাকে আবদ্ধ করার জন্য এটি একটি আঠালো। আকারের উদাহরণটি বাস্তবতা থেকে বিচ্ছিন্ন এবং Circleশ্রেণি একটি দুর্বল নকশা। এই Adapterজাতীয় ক্ষেত্রে আপনার প্যাটার্ন ব্যবহার করা উচিত । দুঃখিত যদি এটি কিছুটা কঠোর মনে হয় তবে Qtউত্তরাধিকার সম্পর্কে রায় দেওয়ার আগে কিছু বাস্তব জীবনের লাইব্রেরি ব্যবহার করার চেষ্টা করুন । উত্তরাধিকার জীবনকে অনেক সহজ করে তোলে।
ডক

2
এটি মোটেও কঠোর মনে হচ্ছে না। কীভাবে আকৃতির উদাহরণ বাস্তবতা থেকে বিচ্ছিন্ন? Adapterপ্যাটার্নটি ব্যবহার করে আপনি সার্কেল ফিক্স করার একটি উদাহরণ (হয়ত আদর্শে) দিতে পারেন ? আমি এর সুবিধাগুলি দেখতে আগ্রহী।
gnzlbg

ঠিক আছে আমি এই ছোট বাক্সে ফিট করার চেষ্টা করব। প্রথমত, আপনি নিজের কাজটি সুরক্ষিত করার জন্য সাধারণত নিজের অ্যাপ্লিকেশন লেখা শুরু করার আগে সাধারণত "মাই শেপ" এর মতো লাইব্রেরি পছন্দ করেন। নইলে আপনি কীভাবে জানতেন Squareযে ইতিমধ্যে সেখানে নেই? দূরদর্শন? এ কারণেই এটি বাস্তবতা থেকে বিচ্ছিন্ন। এবং বাস্তবে আপনি যদি "মাই শেপ" লাইব্রেরির উপর নির্ভর করতে চান তবে আপনি প্রথম থেকেই তার ইন্টারফেসে গ্রহণ করতে পারেন। আকারের উদাহরণে অনেক ননসেন্স রয়েছে (যার একটিতে আপনার দুটি Circleস্ট্রাক্ট রয়েছে), তবে অ্যাডাপ্টারের মতো দেখতে কিছু হবে -> আদর্শ one.com/UogjWk
ডক

2
এটি তখন বাস্তব থেকে বিচ্ছিন্ন নয়। যখন সংস্থা এ সংস্থা বি কিনে এবং সংস্থা বি এর কোডবেসকে এ এর ​​সাথে সংহত করতে চায়, আপনার দুটি সম্পূর্ণ স্বাধীন কোড বেস রয়েছে have কল্পনা করুন যে প্রত্যেকের বিভিন্ন ধরণের শেপ শ্রেণিবিন্যাস রয়েছে। আপনি উত্তরাধিকারের সাথে এগুলিকে সহজেই একত্রিত করতে পারবেন না, এবং সংস্থার সি যুক্ত করুন এবং আপনার একটি বিশাল গণ্ডগোল রয়েছে। আমি মনে করি আপনার এই আলোচনাটি দেখা উচিত: youtube.com/watch?v=0I0FD3N5cgM আমার উত্তরটি পুরানো, তবে আপনি মিলগুলি দেখতে পাবেন। আপনাকে সবসময় সবকিছুর পুনর্বিবেচনা করতে হবে না, আপনি ইন্টারফেসে একটি বাস্তবায়ন সরবরাহ করতে পারেন এবং যদি উপলব্ধ থাকে তবে কোনও সদস্য ফাংশন চয়ন করতে পারেন।
gnzlbg

1
আমি ভিডিওর কিছু অংশ দেখেছি এবং এটি সম্পূর্ণ ভুল। আমি কখনই ডিবাগিং উদ্দেশ্য ব্যতীত ডায়নামিক_কাস্ট ব্যবহার করি না। ডায়নামিক কাস্ট মানে এই ভিডিওটিতে আপনার ডিজাইন এবং ডিজাইনের সাথে কিছু ভুল হয়েছে ডিজাইন :) দ্বারা ভুল। গাই এমনকি Qt- র উল্লেখ করেছেন, তবে এখানেও তিনি ভুল - কিউ লেআউট কিউ উইজেটের কাছ থেকে পায় না বা অন্যভাবে!
ডক

9

উপরে সমস্ত ভাল উত্তর। আপনার অতিরিক্ত একটি জিনিস মনে রাখা উচিত - আপনার খাঁটি ভার্চুয়াল ডেস্ট্রাক্টরও থাকতে পারে। পার্থক্যটি হ'ল আপনার এখনও এটি প্রয়োগ করা দরকার।

বিভ্রান্ত?


    --- header file ----
    class foo {
    public:
      foo() {;}
      virtual ~foo() = 0;

      virtual bool overrideMe() {return false;}
    };

    ---- source ----
    foo::~foo()
    {
    }

আপনি যেটি করতে চাইছেন তার প্রধান কারণ হ'ল যদি আপনি আমার মতো ইন্টারফেসের পদ্ধতিগুলি সরবরাহ করতে চান তবে সেগুলি overচ্ছিকভাবে ওভাররাইড করা।

শ্রেণিটিকে ইন্টারফেস বানাতে একটি খাঁটি ভার্চুয়াল পদ্ধতি প্রয়োজন, তবে আপনার সমস্ত ভার্চুয়াল পদ্ধতিতে ডিফল্ট প্রয়োগ রয়েছে, তাই খাঁটি ভার্চুয়াল তৈরির একমাত্র পদ্ধতিটি হ'ল ডেস্ট্রাক্টর।

উদ্ভূত শ্রেণিতে একজন ডেস্ট্রাক্টরকে পুনর্নির্মাণ করা মোটেও বড় বিষয় নয় - আমি সর্বদা আমার উদ্ভূত ক্লাসগুলিতে একজন ডেস্ট্রাক্টর, ভার্চুয়াল বা না-কে পুনরায় প্রতিস্থাপন করি।


4
কেন, ওহ কেন, কেউ এই ক্ষেত্রে ডার্টটিকে খাঁটি ভার্চুয়াল বানাতে চাইবে? তাতে কী লাভ হবে? আপনি কেবল উদ্ভূত শ্রেণীর উপর এমন কিছু চাপ দিয়েছিলেন যাতে তাদের সম্ভবত অন্তর্ভুক্ত করার দরকার নেই - একটি ডাক্তার।
জোহান জেরেল

6
আপনার প্রশ্নের উত্তর দিতে আমার উত্তর আপডেট। খাঁটি ভার্চুয়াল ডেস্ট্রাক্টর হ'ল একটি বৈধ উপায় (অর্জনের একমাত্র উপায়?) একটি ইন্টারফেস শ্রেণি যেখানে সমস্ত পদ্ধতির ডিফল্ট প্রয়োগ রয়েছে।
রডিল্যান্ড 20

7

আপনি যদি মাইক্রোসফ্টের সি ++ সংকলক ব্যবহার করেন তবে আপনি নিম্নলিখিতগুলি করতে পারেন:

struct __declspec(novtable) IFoo
{
    virtual void Bar() = 0;
};

class Child : public IFoo
{
public:
    virtual void Bar() override { /* Do Something */ }
}

আমি এই পদ্ধতির পছন্দ করি কারণ এটির ফলে অনেকগুলি ছোট ইন্টারফেস কোড আসে এবং উত্পন্ন কোডের আকার উল্লেখযোগ্যভাবে ছোট হতে পারে। অবিশ্বাস্য টেবিলের ব্যবহার সেই শ্রেণীর vtable পয়েন্টারের সমস্ত রেফারেন্স সরিয়ে দেয়, তাই আপনি কখনই সরাসরি তা ইনস্ট্যান্ট করতে পারবেন না। ডকুমেন্টেশন এখানে দেখুন - novtable


4
আপনি কেন novtableস্ট্যান্ডার্ডের চেয়ে বেশি ব্যবহার করেছেন তা আমি বেশ দেখতে পাই নাvirtual void Bar() = 0;
ফ্লেক্সো

2
এটি ছাড়াও (আমি কেবল = 0;যুক্ত করেছি যা আমি হারিয়েছি তা লক্ষ্য করেছি)। ডকুমেন্টেশনটি বুঝতে না পারলে তা পড়ুন।
ইঙ্গ্রাম

আমি এটিটি পড়েই পড়েছি = 0;এবং ধরে নিয়েছি এটি ঠিক একই কাজ করার একটি অ-মানক উপায়।
ফ্লেক্সো

4

সেখানে যা লেখা আছে তার সাথে সামান্য সংযোজন:

প্রথমত, নিশ্চিত করুন যে আপনার ধ্বংসকারীও খাঁটি ভার্চুয়াল

দ্বিতীয়ত, আপনি প্রয়োগ করার সময় আপনি কেবলমাত্র ভাল ব্যবস্থার জন্য কার্যত (সাধারণত না বরং) উত্তরাধিকার সূচনা করতে পারেন।


আমি ভার্চুয়াল উত্তরাধিকার পছন্দ করি কারণ ধারণাগতভাবে এর অর্থ হ'ল উত্তরাধিকারসূত্রে প্রাপ্ত শ্রেণীর একমাত্র উদাহরণ রয়েছে। স্বীকারোক্তিহীনভাবে, এখানে শ্রেণীর কোনও জায়গার প্রয়োজন নেই তাই এটি অতিরিক্ত অতিরিক্ত হতে পারে। আমি কিছুক্ষণের জন্য সি ++ তে এমআই করিনি, তবে কি ভার্চুয়াল উত্তরাধিকারগুলি আপকাস্টিংকে জটিল করবে না?
উরি

কেন, ওহ কেন, কেউ এই ক্ষেত্রে ডার্টটিকে খাঁটি ভার্চুয়াল বানাতে চাইবে? তাতে কী লাভ হবে? আপনি কেবল উদ্ভূত শ্রেণীর উপর এমন কিছু চাপ দিয়েছিলেন যাতে তাদের সম্ভবত অন্তর্ভুক্ত করার দরকার নেই - একটি ডাক্তার।
জোহান জেরেল

2
ইন্টারফেসের পয়েন্টারের মাধ্যমে কোনও অবজেক্টটি ধ্বংস হয়ে যাওয়ার পরিস্থিতি থাকলে আপনার অবশ্যই নিশ্চিত করা উচিত যে ধ্বংসকারীটি ভার্চুয়াল ...
উরি

খাঁটি ভার্চুয়াল ডেস্ট্রাক্টর এর সাথে কোনও ভুল নেই। এটি প্রয়োজনীয় নয়, তবে এতে কোনও ভুল নেই। উদ্ভূত শ্রেণিতে একজন ডেস্ট্রাক্টরকে কার্যকর করা সেই শ্রেণীর বাস্তবায়নকারীদের উপর খুব সম্ভবত একটি বিশাল বোঝা। আপনি কেন এটি করতে চাইলে নীচে আমার উত্তরটি দেখুন।
রডল্যান্ড

ভার্চুয়াল উত্তরাধিকারের জন্য +1, কারণ ইন্টারফেসের সাথে এটি আরও বেশি সম্ভাবনা রয়েছে যে ক্লাসটি দুটি বা ততোধিক পথ থেকে ইন্টারফেসটি নিয়ে আসে। আমি ইন্টারফেসে সুরক্ষিত ধ্বংসকারীদের জন্য বেছে নিই।
ডক্ট

4

আপনি এনভিআই (নন ভার্চুয়াল ইন্টারফেস প্যাটার্ন) এর সাথে বাস্তবায়িত চুক্তি ক্লাসগুলিও বিবেচনা করতে পারেন। এই ক্ষেত্রে:

struct Contract1 : boost::noncopyable
{
    virtual ~Contract1();
    void f(Parameters p) {
        assert(checkFPreconditions(p)&&"Contract1::f, pre-condition failure");
        // + class invariants.
        do_f(p);
        // Check post-conditions + class invariants.
    }
private:
    virtual void do_f(Parameters p) = 0;
};
...
class Concrete : public Contract1, public Contract2
{
private:
    virtual void do_f(Parameters p); // From contract 1.
    virtual void do_g(Parameters p); // From contract 2.
};

অন্যান্য পাঠকদের জন্য, ড ডবস নিবন্ধ "কথোপকথন: ভার্চুয়ালি আপনার" জিম হিসলপ এবং হার্ব সুতার রচনায় কেন কেউ এনভিআই ব্যবহার করতে চাইতে পারে সে সম্পর্কে আরও কিছুটা ব্যাখ্যা করেছেন।
ব্যবহারকারী2067021

এবং এই ভেষজ সুতার রচনা "ভার্চুয়ালতা"।
ব্যবহারকারী2067021

1

আমি সি ++ বিকাশে এখনও নতুন। আমি ভিজ্যুয়াল স্টুডিও (ভিএস) দিয়ে শুরু করেছি।

তবুও, __interfaceভিএস (। নেট) তে কেউ উল্লেখ করেছেন বলে মনে হয় না । আমি না খুব নিশ্চিত যদি এই একটি ভাল উপায় হল একটি ইন্টারফেস ঘোষণা হয়। তবে মনে হয় এটি একটি অতিরিক্ত প্রয়োগকারী সরবরাহ করে ( নথিতে উল্লিখিত )। এটি আপনাকে স্বতঃস্ফূর্তভাবে নির্দিষ্ট করতে হবে না virtual TYPE Method() = 0;, যেহেতু এটি স্বয়ংক্রিয়ভাবে রূপান্তরিত হবে।

__interface IMyInterface {
   HRESULT CommitX();
   HRESULT get_X(BSTR* pbstrName);
};

তবে আমি এটি ব্যবহার করি না কারণ আমি ক্রস প্ল্যাটফর্ম সংকলন সামঞ্জস্যতা নিয়ে উদ্বিগ্ন, কারণ এটি কেবলমাত্র নেট। এর অধীনে উপলব্ধ।

কারও কাছে এটি সম্পর্কে আকর্ষণীয় কিছু থাকলে দয়া করে শেয়ার করুন। :-)

ধন্যবাদ।


0

যদিও এটি সত্য যে virtualকোনও ইন্টারফেসটি সংজ্ঞায়িত করার জন্য ডি-ফ্যাক্টো স্ট্যান্ডার্ড, আসুন আমরা ক্লাসিক সি-জাতীয় প্যাটার্নটি ভুলে যাব না, যা সি ++ তে একটি নির্মাণকারীর সাথে আসে:

struct IButton
{
    void (*click)(); // might be std::function(void()) if you prefer

    IButton( void (*click_)() )
    : click(click_)
    {
    }
};

// call as:
// (button.*click)();

এটির সুবিধাটি রয়েছে যে আপনি নিজের ক্লাসটি আবার তৈরি না করেই ইভেন্ট রানটাইমটিকে পুনরায় বাঁধতে পারবেন (যেমন সি ++ তে বহুবর্ষীয় ধরণের পরিবর্তনের জন্য একটি সিনট্যাক্স নেই, এটি গিরগিটি শ্রেণীর জন্য কার্যকর)।

পরামর্শ:

  • আপনি এটিকে বেস ক্লাস হিসাবে উত্তরাধিকারী হতে পারেন (ভার্চুয়াল এবং অ-ভার্চুয়াল উভয়েরই অনুমতি রয়েছে) এবং clickআপনার বংশধরের কনস্ট্রাক্টর পূরণ করুন ।
  • আপনার protectedসদস্য হিসাবে ফাংশন পয়েন্টার থাকতে পারে এবং আপনার একটি publicরেফারেন্স এবং / অথবা গিটার থাকতে পারে।
  • উপরে উল্লিখিত হিসাবে, এটি আপনাকে রানটাইমে বাস্তবায়ন পরিবর্তন করতে দেয়। সুতরাং এটি রাষ্ট্র পরিচালনা করারও একটি উপায়। ifআপনার কোডে ও বনামের রাজ্যের পরিবর্তনের সংখ্যার উপর নির্ভর করে এটি এস বা এসের চেয়ে দ্রুত হতে পারে (টার্নআরউন্ডটি ৩-৪ সেকেন্ডের কাছাকাছি আশা করা যায় , তবে সর্বদা প্রথমে পরিমাপ করুন।switch()ifif
  • আপনি যদি std::function<>ফাংশন পয়েন্টার উপর, আপনি পারে মধ্যে আপনার সমস্ত অবজেক্ট ডেটা পরিচালনা করতে পারবেন IBase। এই বিন্দু থেকে, আপনার জন্য মান স্কিমেটিক্স থাকতে পারে IBase(যেমন, std::vector<IBase>কাজ করবে)। নোট করুন যে এটি আপনার সংকলক এবং এসটিএল কোডের উপর নির্ভর করে ধীর হতে পারে; std::function<>ফাংশন পয়েন্টার বা এমনকি ভার্চুয়াল ফাংশনগুলির সাথে তুলনায় যখন বর্তমান বাস্তবায়নগুলি একটি ওভারহেড ঝোঁক দেয় (এটি ভবিষ্যতে পরিবর্তিত হতে পারে)।

0

এখানে সংজ্ঞা দেওয়া হল abstract class সি ++ স্ট্যান্ডার্ডের আছে

n4687

13.4.2

একটি বিমূর্ত শ্রেণি একটি শ্রেণি যা কেবলমাত্র অন্য কোনও শ্রেণির বেস শ্রেণি হিসাবে ব্যবহার করা যেতে পারে; বিমূর্ত শ্রেণীর কোনও বস্তু এটি থেকে উত্পন্ন ক্লাসের সাবোবজেক্টগুলি ছাড়া তৈরি করা যায় না। কোনও শ্রেণীর বিমূর্ত থাকে যদি এতে কমপক্ষে একটি খাঁটি ভার্চুয়াল ফাংশন থাকে।


-2
class Shape 
{
public:
   // pure virtual function providing interface framework.
   virtual int getArea() = 0;
   void setWidth(int w)
   {
      width = w;
   }
   void setHeight(int h)
   {
      height = h;
   }
protected:
    int width;
    int height;
};

class Rectangle: public Shape
{
public:
    int getArea()
    { 
        return (width * height); 
    }
};
class Triangle: public Shape
{
public:
    int getArea()
    { 
        return (width * height)/2; 
    }
};

int main(void)
{
     Rectangle Rect;
     Triangle  Tri;

     Rect.setWidth(5);
     Rect.setHeight(7);

     cout << "Rectangle area: " << Rect.getArea() << endl;

     Tri.setWidth(5);
     Tri.setHeight(7);

     cout << "Triangle area: " << Tri.getArea() << endl; 

     return 0;
}

ফলাফল: আয়তক্ষেত্র অঞ্চল: 35 ত্রিভুজ অঞ্চল: 17

আমরা দেখেছি যে কীভাবে একটি বিমূর্ত শ্রেণি getArea () এবং অন্য দুটি শ্রেণীর ক্ষেত্রে একটি ইন্টারফেস সংজ্ঞায়িত করে একই ফাংশন বাস্তবায়িত করেছে তবে আকারের সাথে নির্দিষ্ট ক্ষেত্রটি গণনা করার জন্য বিভিন্ন অ্যালগরিদম সহ।


5
এটিই ইন্টারফেস হিসাবে বিবেচিত হয় না! এটি কেবলমাত্র একটি পদ্ধতি সহ একটি বিমূর্ত বেস ক্লাস যা ওভাররাইড করা প্রয়োজন! ইন্টারফেসগুলি সাধারণত এমন বস্তু যা কেবল পদ্ধতির সংজ্ঞা থাকে - একটি "চুক্তি" অন্যান্য শ্রেণিগুলি যখন ইন্টারফেসটি প্রয়োগ করে তখন তা পূরণ করতে হয়।
গিটারফ্লো ২
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.