আমাদের কেন সি ++ এ ভার্চুয়াল ফাংশন প্রয়োজন?


1311

আমি সি ++ শিখছি এবং আমি কেবল ভার্চুয়াল ফাংশনগুলিতে প্রবেশ করছি।

আমি যা পড়েছি (বই এবং অনলাইনে) থেকে, ভার্চুয়াল ফাংশনগুলি বেস ক্লাসে এমন ফাংশন যা আপনি উত্পন্ন ক্লাসগুলিতে ওভাররাইড করতে পারেন।

তবে বইয়ের আগে, যখন মৌলিক উত্তরাধিকার সম্পর্কে শিখছিলাম তখন আমি ব্যবহার না করে উত্পন্ন শ্রেণিতে বেস ফাংশনগুলিকে ওভাররাইড করতে সক্ষম হয়েছি virtual

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


13
: আমি এখানে ভার্চুয়াল কাজকর্মের জন্য বাস্তবসম্মত ব্যাখ্যা তৈরি করেছি nrecursions.blogspot.in/2015/06/...
এনএভি

4
এটি সম্ভবত ভার্চুয়াল ফাংশনগুলির সবচেয়ে বড় সুবিধা - আপনার কোডটি এমনভাবে গঠনের ক্ষমতা যাতে নতুন উত্পন্ন ক্লাসগুলি স্বয়ংক্রিয়ভাবে পুরানো কোডটি পরিবর্তন না করেই কাজ করবে!
ব্যবহারকারী 3530616

tbh, ভার্চুয়াল ফাংশনগুলি মুছে ফেলার জন্য ওওপির প্রধান বৈশিষ্ট্য feature আমি মনে করি, এটি অ-ভার্চুয়াল পদ্ধতিগুলি হ'ল অবজেক্ট পাস্কল এবং সি ++ বিশেষ করে তোলে, অপ্রয়োজনীয় বড় ভিটিবেলের অপ্টিমাইজেশন এবং পিওড-সামঞ্জস্যপূর্ণ শ্রেণিগুলিকে অনুমতি দেয়। অনেকগুলি ওওপি ভাষা প্রত্যাশা করে যে প্রতিটি পদ্ধতি ওভাররেড করা যেতে পারে।
সুইফ্ট - শুক্রবার পাই

এটা একটা ভালো প্রশ্ন। প্রকৃতপক্ষে সি ++ এ এই ভার্চুয়াল জিনিসটি জাভা বা পিএইচপি এর মতো অন্যান্য ভাষায় বিমূর্ত হয়ে যায়। সি ++ এ আপনি কিছু বিরল ক্ষেত্রে (কেবল একাধিক উত্তরাধিকার বা ডিডিওডের সেই বিশেষ ক্ষেত্রে সচেতন হন ) আরও কিছুটা নিয়ন্ত্রণ পান । তবে কেন এই প্রশ্নটি স্ট্যাকওভারফ্লো ডটকম এ পোস্ট করা হয়েছে?
এডগার অ্যালোরো

আমি মনে করি আপনি প্রারম্ভিক বাঁধাই-দেরী বন্ধন এবং VTABLE এ একবার নজর রাখলে এটি আরও যুক্তিসঙ্গত হবে এবং বোধগম্য হবে। সুতরাং এখানে একটি ভাল ব্যাখ্যা রয়েছে ( learnncpp.com/cpp-tutorial/125-the- ভার্চুয়াল- টেবিল )।
সিইন

উত্তর:


2728

এখানে কীভাবে আমি বুঝতে পারি যে কী কী virtualকাজগুলি তা নয়, তবে কেন তাদের প্রয়োজনীয়তা রয়েছে:

ধরা যাক আপনার এই দুটি ক্লাস রয়েছে:

class Animal
{
    public:
        void eat() { std::cout << "I'm eating generic food."; }
};

class Cat : public Animal
{
    public:
        void eat() { std::cout << "I'm eating a rat."; }
};

আপনার প্রধান কাজ:

Animal *animal = new Animal;
Cat *cat = new Cat;

animal->eat(); // Outputs: "I'm eating generic food."
cat->eat();    // Outputs: "I'm eating a rat."

এখন পর্যন্ত এত ভাল, তাই না? প্রাণী জেনেরিক খাবার খায়, বিড়ালরা ইঁদুর খায়, সব ছাড়াই virtual

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

// This can go at the top of the main.cpp file
void func(Animal *xyz) { xyz->eat(); }

এখন আমাদের মূল কাজটি হ'ল:

Animal *animal = new Animal;
Cat *cat = new Cat;

func(animal); // Outputs: "I'm eating generic food."
func(cat);    // Outputs: "I'm eating generic food."

ওহ ... আমরা একটি বিড়াল পেরিয়েছি func(), তবে এটি ইঁদুর খাবে না। আপনার ওভারলোড করা উচিত func()তাই এটি লাগে Cat*? আপনার যদি প্রাণী থেকে আরও প্রাণী সংগ্রহ করতে হয় তবে তাদের সমস্তের নিজস্ব প্রয়োজন func()

ক্লাস eat()থেকে Animalভার্চুয়াল ফাংশন তৈরির সমাধানটি হ'ল :

class Animal
{
    public:
        virtual void eat() { std::cout << "I'm eating generic food."; }
};

class Cat : public Animal
{
    public:
        void eat() { std::cout << "I'm eating a rat."; }
};

প্রধান:

func(animal); // Outputs: "I'm eating generic food."
func(cat);    // Outputs: "I'm eating a rat."

সম্পন্ন.


164
সুতরাং যদি আমি এটি সঠিকভাবে বুঝতে পারি তবে ভার্চুয়াল সাবক্লাস পদ্ধতিটি কল করার অনুমতি দেয়, এমনকি যদি বস্তুকে তার সুপারক্লাস হিসাবে বিবেচনা করা হয় তবে?
কেনি ওয়ার্ডেন

146
কোনও মধ্যস্থতাকারী ফাংশন "ফানক" এর উদাহরণ দিয়ে দেরী বন্ধন ব্যাখ্যা করার পরিবর্তে এখানে আরও সরল বিক্ষোভ প্রদর্শন করা হচ্ছে - অ্যানিমাল * অ্যানিম্যাল = নতুন অ্যানিম্যাল; // বিড়াল * বিড়াল = নতুন বিড়াল; প্রাণী * বিড়াল = নতুন বিড়াল; পশু> খাওয়া (); // আউটপুটস: "আমি জেনেরিক খাবার খাচ্ছি।" cat-> খাওয়া (); // আউটপুটস: "আমি জেনেরিক খাবার খাচ্ছি।" আপনি উপ-শ্রেণিবদ্ধ বস্তু (বিড়াল) বরাদ্দ দিচ্ছেন, তবুও পদ্ধতিটি আহ্বান করা হচ্ছে পয়েন্টার টাইপের (প্রাণীর) উপর ভিত্তি করে এটি নির্দেশিত বস্তুর ধরণ নয়। এজন্য আপনার "ভার্চুয়াল" দরকার।
রেক্সবেলিয়া

37
আমি কি কেবলমাত্র সি ++ এ এই ডিফল্ট আচরণটি খুঁজে বেড়াচ্ছি? "ভার্চুয়াল" ছাড়াই কোডটি কাজ করার আশা করতাম।
ডেভিড 天宇 ওয়াং

20
@ ডেভিড 天宇 ওয়াং আমার কাছে মনে virtualহয় কিছু স্থিতিশীল বাঁধাই বনাম স্থিতাবস্থা প্রবর্তন করা হয়েছে এবং হ্যাঁ আপনি জাভার মতো ভাষা থেকে এসে থাকেন তবে এটি অদ্ভুত।
পিটারচাউলা

32
প্রথমত, ভার্চুয়াল কলগুলি নিয়মিত ফাংশন কলগুলির চেয়ে অনেক বেশি ব্যয়বহুল। সি ++ দর্শন ডিফল্টরূপে দ্রুত, তাই ডিফল্টরূপে ভার্চুয়াল কলগুলি বড় সংখ্যা নয়। দ্বিতীয় কারণ হ'ল ভার্চুয়াল কলগুলি আপনার কোডটি ভঙ্গ করতে পারে যদি আপনি কোনও লাইব্রেরি থেকে কোনও শ্রেণীর উত্তরাধিকারী হন এবং এটি বেস শ্রেণীর আচরণ পরিবর্তন না করে কোনও পাবলিক বা প্রাইভেট পদ্ধতিতে (যা অভ্যন্তরীণে একটি ভার্চুয়াল পদ্ধতি বলে) এর অভ্যন্তরীণ বাস্তবায়ন পরিবর্তন করে।
saolof

671

"ভার্চুয়াল" ছাড়াই আপনি "প্রারম্ভিক বাঁধাই" পাবেন। পদ্ধতির কোন বাস্তবায়ন ব্যবহৃত হবে তা আপনি যে পয়েন্টারের মাধ্যমে কল করেছেন তার ধরণের ভিত্তিতে সংকলন সময়ে সিদ্ধান্ত নেওয়া হবে।

"ভার্চুয়াল" এর সাহায্যে আপনি "লেট বাইন্ডিং" পাবেন। পদ্ধতির কোন বাস্তবায়ন ব্যবহৃত হবে তা পয়েন্ট-টু অবজেক্টের ধরণের ভিত্তিতে রান সময়ে সিদ্ধান্ত নেওয়া হয় - এটি মূলত কী হিসাবে নির্মিত হয়েছিল। আপনি যে বিষয়টির দিকে লক্ষ্য রেখেছেন সেই ধরণের পয়েন্টারের ধরণের ভিত্তিতে এটি কী প্রয়োজন তা অগত্যা নয়।

class Base
{
  public:
            void Method1 ()  {  std::cout << "Base::Method1" << std::endl;  }
    virtual void Method2 ()  {  std::cout << "Base::Method2" << std::endl;  }
};

class Derived : public Base
{
  public:
    void Method1 ()  {  std::cout << "Derived::Method1" << std::endl;  }
    void Method2 ()  {  std::cout << "Derived::Method2" << std::endl;  }
};

Base* obj = new Derived ();
  //  Note - constructed as Derived, but pointer stored as Base*

obj->Method1 ();  //  Prints "Base::Method1"
obj->Method2 ();  //  Prints "Derived::Method2"

সম্পাদনা করুন - এই প্রশ্নটি দেখুন

এছাড়াও - এই টিউটোরিয়ালটি সি ++ এ প্রথম এবং দেরীতে আবদ্ধ হয় covers


11
দুর্দান্ত, এবং দ্রুত এবং আরও ভাল উদাহরণ ব্যবহার করে ঘরে ফিরে যায়। এটি অবশ্য সরল, এবং প্রশ্নকারীকে সত্যই কেবল প্যারাসিফট . com/c++-faq-lite/virtual-function.html পৃষ্ঠাটি পড়া উচিত । অন্যান্য লোকেরা ইতিমধ্যে এই থ্রেড থেকে যুক্ত এসও নিবন্ধগুলিতে এই সংস্থানটির দিকে ইঙ্গিত করেছে, তবে আমি বিশ্বাস করি এটি পুনরায় উল্লেখ করার মতো।
সনি

36
আমি জানি না যে প্রারম্ভিক এবং দেরীতে বাইন্ডিংটি নির্দিষ্টভাবে সি ++ সম্প্রদায়ে ব্যবহৃত শর্তাদি ব্যবহৃত হয় তবে সঠিক পদগুলি স্থির (সংকলনের সময়) এবং গতিশীল (রানটাইম এ) বাঁধাই হয়।
মাইক

31
@ মাইক - "দেরী বাইন্ডিং" শব্দটি অন্তত 1960 এর দশকের, যেখানে এটি এসিএমের যোগাযোগগুলিতে পাওয়া যায়। " । প্রতিটি ধারণার জন্য একটি সঠিক শব্দ থাকলে এটি ভাল হবে না? দুর্ভাগ্যক্রমে, এটি ঠিক তেমন কিছু নয়। "প্রারম্ভিক বাঁধাই" এবং "দেরি বাইন্ডিং" পদগুলি সি ++ এবং এমনকি অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিং প্রেস্টেট করে এবং আপনি যে শর্তাদি ব্যবহার করেন ঠিক ঠিক ততটাই সঠিক।
স্টিভ 314

4
@ বিজেভকে - এই উত্তরটি সি ++ 11 প্রকাশের আগে লেখা হয়েছিল। এমনকি, তাই আমি এটা জিসিসি 6.3.0 মধ্যে কম্পাইল করা (গ ব্যবহার ++, 14 ডিফল্ট অনুসারে) কোন সমস্যার সঙ্গে - স্পষ্টত একটি পরিবর্তনশীল ঘোষণা এবং কলের মোড়কে mainফাংশন ইত্যাদি পয়েন্টার টু উদ্ভূত পরোক্ষভাবে থেকে কাস্ট পয়েন্টার টু বেস (আরও বিশেষজ্ঞ স্পষ্টত আরও সাধারণকে কাস্ট করেন)। ভিসা-বিপরীতে আপনার একটি সুস্পষ্ট কাস্ট প্রয়োজন, সাধারণত এ dynamic_cast। অন্য যে কোনও কিছুই - অপরিজ্ঞাত আচরণের জন্য খুব প্রবণ তাই নিশ্চিত হয়ে নিন যে আপনি কী করছেন। আমার জ্ঞানের সেরা, সি ++ 98 এর আগে থেকে এটি পরিবর্তন হয়নি।
স্টিভ 314

10
নোট করুন যে সি ++ সংকলকরা প্রায়শই প্রথম দিকে দেরিতে প্রাথমিকভাবে বাঁধাইয়ের জন্য অনুকূলিত করতে পারেন - যখন তারা বাইন্ডিংটি কী হতে চলেছে তা সম্পর্কে নিশ্চিত হতে পারে। এটিকে "ডি-ভার্চুয়ালাইজেশন" হিসাবেও উল্লেখ করা হয়।
einpoklum

83

এটি প্রদর্শনের জন্য আপনার কমপক্ষে 1 স্তরের উত্তরাধিকার এবং একটি ডাউনস্টক প্রয়োজন। এখানে একটি খুব সাধারণ উদাহরণ:

class Animal
{        
    public: 
      // turn the following virtual modifier on/off to see what happens
      //virtual   
      std::string Says() { return "?"; }  
};

class Dog: public Animal
{
    public: std::string Says() { return "Woof"; }
};

void test()
{
    Dog* d = new Dog();
    Animal* a = d;       // refer to Dog instance with Animal pointer

    std::cout << d->Says();   // always Woof
    std::cout << a->Says();   // Woof or ?, depends on virtual
}

39
আপনার উদাহরণ বলছে যে ফিরে আসা স্ট্রিংটি ফাংশনটি ভার্চুয়াল কিনা তার উপর নির্ভর করে তবে কোন ফলাফলটি ভার্চুয়ালের সাথে মিলিত হয় এবং কোনটি ভার্চুয়াল নন-ভার্চুয়াল correspond অতিরিক্ত হিসাবে, আপনি যে স্ট্রিংটি ফিরে আসছেন তা ব্যবহার না করায় এটি কিছুটা বিভ্রান্তিকর।
রস 11

7
ভার্চুয়াল কীওয়ার্ড সহ: ওউফ । ভার্চুয়াল শব্দ ছাড়া: ?
হেশাম এরাকী

@ ভার্চুয়াল ছাড়াই হেশাম ইরাকী এটি তাড়াতাড়ি বাঁধাই এবং এটি "প্রদর্শিত হবে?" বেস ক্লাসের
আহমদ

46

নিরাপদ ডাউনকাস্টিং , সরলতা এবং সংক্ষিপ্ততার জন্য আপনার ভার্চুয়াল পদ্ধতিগুলির প্রয়োজন ।

ভার্চুয়াল পদ্ধতিগুলি এগুলি করে: এগুলি আপাতত সহজ এবং সংক্ষিপ্ত কোড সহ নিরাপদে সুরক্ষিত হয় এবং আপনার অন্যথায় যে জটিল এবং ভার্জোজ কোড থাকে তা অনিরাপদ ম্যানুয়াল ক্যাসেটগুলি এড়িয়ে চলে।


অ-ভার্চুয়াল পদ্ধতি ⇒ স্ট্যাটিক বাঁধাই

নিম্নলিখিত কোডটি ইচ্ছাকৃতভাবে "ভুল"। এটি valueপদ্ধতিটিকে এই হিসাবে ঘোষণা করে না virtualএবং তাই অনিচ্ছাকৃত "ভুল" ফলাফল তৈরি করে, যথা: 0:

#include <iostream>
using namespace std;

class Expression
{
public:
    auto value() const
        -> double
    { return 0.0; }         // This should never be invoked, really.
};

class Number
    : public Expression
{
private:
    double  number_;

public:
    auto value() const
        -> double
    { return number_; }     // This is OK.

    Number( double const number )
        : Expression()
        , number_( number )
    {}
};

class Sum
    : public Expression
{
private:
    Expression const*   a_;
    Expression const*   b_;

public:
    auto value() const
        -> double
    { return a_->value() + b_->value(); }       // Uhm, bad! Very bad!

    Sum( Expression const* const a, Expression const* const b )
        : Expression()
        , a_( a )
        , b_( b )
    {}
};

auto main() -> int
{
    Number const    a( 3.14 );
    Number const    b( 2.72 );
    Number const    c( 1.0 );

    Sum const       sum_ab( &a, &b );
    Sum const       sum( &sum_ab, &c );

    cout << sum.value() << endl;
}

লাইনে মন্তব্য করা হয়েছে "খারাপ" Expression::valueপদ্ধতিটিকে বলা হয়, কারণ স্ট্যাটিকালি পরিচিত টাইপ (সংকলনের সময় পরিচিত টাইপ) হয় Expression, এবং valueপদ্ধতিটি ভার্চুয়াল নয়।


ভার্চুয়াল পদ্ধতি ⇒ গতিশীল বাঁধাই।

স্থিতিশীলভাবে পরিচিত ধরণের valueহিসাবে ঘোষণা করা নিশ্চিত করে যে প্রতিটি কল virtualএটির Expressionপ্রকৃত ধরণের কী জিনিস তা পরীক্ষা করবে এবং valueসেই গতিশীল প্রকারের জন্য প্রাসঙ্গিক বাস্তবায়নকে কল করবে :

#include <iostream>
using namespace std;

class Expression
{
public:
    virtual
    auto value() const -> double
        = 0;
};

class Number
    : public Expression
{
private:
    double  number_;

public:
    auto value() const -> double
        override
    { return number_; }

    Number( double const number )
        : Expression()
        , number_( number )
    {}
};

class Sum
    : public Expression
{
private:
    Expression const*   a_;
    Expression const*   b_;

public:
    auto value() const -> double
        override
    { return a_->value() + b_->value(); }    // Dynamic binding, OK!

    Sum( Expression const* const a, Expression const* const b )
        : Expression()
        , a_( a )
        , b_( b )
    {}
};

auto main() -> int
{
    Number const    a( 3.14 );
    Number const    b( 2.72 );
    Number const    c( 1.0 );

    Sum const       sum_ab( &a, &b );
    Sum const       sum( &sum_ab, &c );

    cout << sum.value() << endl;
}

এখানে আউটপুট 6.86হিসাবে এটি হওয়া উচিত, ভার্চুয়াল পদ্ধতি যেহেতু কার্যত নামক । একে কলগুলির গতিশীল বাঁধাইও বলা হয় । সামান্য চেক করা হয়, প্রকৃত গতিশীল ধরণের অবজেক্টের সন্ধান করে এবং সেই গতিশীল প্রকারের জন্য প্রাসঙ্গিক পদ্ধতি বাস্তবায়ন বলা হয়।

প্রাসঙ্গিক বাস্তবায়ন সর্বাধিক সুনির্দিষ্ট (সর্বাধিক উত্পন্ন) শ্রেণীর মধ্যে একটি।

লক্ষ্য করুন উদ্ভূত শ্রেণীর এখানে পদ্ধতি বাস্তবায়নের হিসাবে চিহ্নিত করা হয় না virtual, কিন্তু এর পরিবর্তে চিহ্নিত করা হয়েছে override। এগুলিকে চিহ্নিত করা যেতে পারে virtualতবে তারা স্বয়ংক্রিয়ভাবে ভার্চুয়াল're overrideশব্দ নিশ্চিত করে যে আছে যদি না কিছু বেস ক্লাসে যেমন একটি ভার্চুয়াল পদ্ধতি, তাহলে আপনি একটি ত্রুটি (যা কাম্য) পাবেন।


ভার্চুয়াল পদ্ধতি ছাড়াই এটি করার কদর্যতা

virtualএক ছাড়া ডায়নামিক বাইন্ডিংয়ের কিছু করুন এটি স্বয়ংক্রিয় সংস্করণ প্রয়োগ করতে হবে । এটি এটির মধ্যে সাধারণত অনিরাপদ ম্যানুয়াল ডাউন কাস্টিং, জটিলতা এবং ভার্বোসিসিটি জড়িত।

একক ফাংশনের ক্ষেত্রে যেমন এখানে রয়েছে তবে অবজেক্টে কোনও ফাংশন পয়েন্টার সংরক্ষণ এবং সেই ফাংশন পয়েন্টারটির মাধ্যমে কল করা যথেষ্ট, তবে এটির মধ্যে কিছু অনিরাপদ ডাউনকাস্ট, জটিলতা এবং ভার্বোসিসিটি জড়িত রয়েছে:

#include <iostream>
using namespace std;

class Expression
{
protected:
    typedef auto Value_func( Expression const* ) -> double;

    Value_func* value_func_;

public:
    auto value() const
        -> double
    { return value_func_( this ); }

    Expression(): value_func_( nullptr ) {}     // Like a pure virtual.
};

class Number
    : public Expression
{
private:
    double  number_;

    static
    auto specific_value_func( Expression const* expr )
        -> double
    { return static_cast<Number const*>( expr )->number_; }

public:
    Number( double const number )
        : Expression()
        , number_( number )
    { value_func_ = &Number::specific_value_func; }
};

class Sum
    : public Expression
{
private:
    Expression const*   a_;
    Expression const*   b_;

    static
    auto specific_value_func( Expression const* expr )
        -> double
    {
        auto const p_self  = static_cast<Sum const*>( expr );
        return p_self->a_->value() + p_self->b_->value();
    }

public:
    Sum( Expression const* const a, Expression const* const b )
        : Expression()
        , a_( a )
        , b_( b )
    { value_func_ = &Sum::specific_value_func; }
};


auto main() -> int
{
    Number const    a( 3.14 );
    Number const    b( 2.72 );
    Number const    c( 1.0 );

    Sum const       sum_ab( &a, &b );
    Sum const       sum( &sum_ab, &c );

    cout << sum.value() << endl;
}

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


40

ভারচুয়াল ফাংশনগুলি রানটাইম পলিমারফিজম সমর্থন করতে ব্যবহৃত হয় ।

অর্থাৎ ভার্চুয়াল কীওয়ার্ড সংকলককে সংকলনের সময় সিদ্ধান্ত গ্রহণ (ফাংশন বাইন্ডিং) না করার জন্য বলে, বরং রানটাইমের জন্য স্থগিত করে "

  • আপনি virtualএর বেস শ্রেণীর ঘোষণায় মূল শব্দটির আগে একটি ফাংশন ভার্চুয়াল তৈরি করতে পারেন । উদাহরণ স্বরূপ,

     class Base
     {
        virtual void func();
     }
  • যখন একটি বেস ক্লাসের ভার্চুয়াল সদস্য ফাংশন থাকে, বেস ক্লাস থেকে উত্তরাধিকারসূত্রে প্রাপ্ত যে কোনও শ্রেণীর নতুন সংজ্ঞা দিতে পারে সঙ্গে ফাংশন ঠিক একই প্রোটোটাইপ অর্থাত শুধুমাত্র কার্যকারিতা, পুনরায় সংজ্ঞায়িত করা যেতে পারে ফাংশনের না ইন্টারফেস।

     class Derive : public Base
     {
        void func();
     }
  • বেস ক্লাস পয়েন্টারটি বেস ক্লাস অবজেক্টের পাশাপাশি ডেরিভড ক্লাস অবজেক্টকে নির্দেশ করতে ব্যবহার করা যেতে পারে।

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

34

যদি বেস ক্লাস হয় Base, এবং একটি উদ্ভূত বর্গ হয় Der, আপনার একটি Base *pপয়েন্টার থাকতে পারে যা প্রকৃতপক্ষে একটি উদাহরণকে নির্দেশ করে Der। আপনি কল যখন p->foo();, যদি fooহয় না ভার্চুয়াল, তারপর Baseএটি-এর সংস্করণ executes, আসলে উপেক্ষা pআসলে একটি পয়েন্ট Der। যদি foo ভার্চুয়াল হয় তবে পয়েন্ট-টু আইটেমের আসল শ্রেণিকে পুরোপুরি p->foo()বিবেচনায় নিয়ে "লিফটমাস্ট" ওভাররাইড কার্যকর করে foo। সুতরাং ভার্চুয়াল এবং অ-ভার্চুয়াল মধ্যে পার্থক্য আসলে বেশ গুরুত্বপূর্ণ: প্রাক্তন রানটাইম পলিমারফিজম , ওও প্রোগ্রামিংয়ের মূল ধারণাটিকে অনুমোদন দেয় , যদিও পরবর্তীটি তা করে না।


8
আমি আপনার বিরোধিতা করতে ঘৃণা করি, তবে সংকলন-কাল পলিমারফিজম এখনও বহুকর্ম। এমনকি অ-সদস্যবিহীন ফাংশনগুলি ওভারলোডিং বহুবিজ্ঞানের একধরনের রূপ - আপনার লিঙ্কে পরিভাষা ব্যবহার করে অ্যাড-হক পলিমারফিজম। এখানে পার্থক্যটি প্রাথমিক এবং দেরী বন্ধনের মধ্যে।
স্টিভ 314

7
@ স্টিভ 314, আপনি পেডেন্টালি সঠিকভাবে (সহযোদ্ধা হিসাবে, আমি যে অনুমোদিত ;-) - অনুপস্থিত বিশেষণ যোগ করতে উত্তর সম্পাদনা ;-)।
অ্যালেক্স মার্টেলি

26

ভার্চুয়াল ফাংশনটির প্রয়োজনীয়তা ব্যাখ্যা করা হয়েছে [বোঝার সহজ]

#include<iostream>

using namespace std;

class A{
public: 
        void show(){
        cout << " Hello from Class A";
    }
};

class B :public A{
public:
     void show(){
        cout << " Hello from Class B";
    }
};


int main(){

    A *a1 = new B; // Create a base class pointer and assign address of derived object.
    a1->show();

}

আউটপুট হবে:

Hello from Class A.

তবে ভার্চুয়াল ফাংশন সহ:

#include<iostream>

using namespace std;

class A{
public:
    virtual void show(){
        cout << " Hello from Class A";
    }
};

class B :public A{
public:
    virtual void show(){
        cout << " Hello from Class B";
    }
};


int main(){

    A *a1 = new B;
    a1->show();

}

আউটপুট হবে:

Hello from Class B.

অতএব ভার্চুয়াল ফাংশন দিয়ে আপনি রানটাইম পলিমারফিজম অর্জন করতে পারেন।


25

আমি ভার্চুয়াল ফাংশনটির আরেকটি ব্যবহার যুক্ত করতে চাই যদিও এটি উপরে বর্ণিত উত্তরের মতো একই ধারণাটি ব্যবহার করে তবে আমি এটির উল্লেখ করার মতো অনুমান করি।

ভার্চুয়াল ডিস্ট্রাক্টর

বেস ক্লাসের ডেস্ট্রাক্টরকে ভার্চুয়াল হিসাবে ঘোষণা না করে নীচের এই প্রোগ্রামটি বিবেচনা করুন; বিড়ালের জন্য মেমরি পরিষ্কার নাও হতে পারে।

class Animal {
    public:
    ~Animal() {
        cout << "Deleting an Animal" << endl;
    }
};
class Cat:public Animal {
    public:
    ~Cat() {
        cout << "Deleting an Animal name Cat" << endl;
    }
};

int main() {
    Animal *a = new Cat();
    delete a;
    return 0;
}

আউটপুট:

Deleting an Animal
class Animal {
    public:
    virtual ~Animal() {
        cout << "Deleting an Animal" << endl;
    }
};
class Cat:public Animal {
    public:
    ~Cat(){
        cout << "Deleting an Animal name Cat" << endl;
    }
};

int main() {
    Animal *a = new Cat();
    delete a;
    return 0;
}

আউটপুট:

Deleting an Animal name Cat
Deleting an Animal

11
without declaring Base class destructor as virtual; memory for Cat may not be cleaned up.এটি তার চেয়েও খারাপ। বেস পয়েন্টার / রেফারেন্সের মাধ্যমে উত্পন্ন বস্তুটি মুছে ফেলা খাঁটি অপরিজ্ঞাত আচরণ। সুতরাং, কিছু মেমরি ফাঁস হতে পারে তা কেবল নয়। বরং, প্রোগ্রাম অসুস্থ গঠিত হয়, তাই কম্পাইলার কিছু সেটিকে রুপান্তর করতে পারেন: মেশিন কোড যে ঘটবে কাজ জরিমানা, অথবা আপনার নাক থেকে কিছুই, অথবা সমন ভূত না, বা ইত্যাদি যে কেন, যদি একটি প্রোগ্রাম যেমন মধ্যে নির্মিত হয়েছে কোনও উপায়ে কোনও ব্যবহারকারী কোনও বেস রেফারেন্সের মাধ্যমে উত্পন্ন উদাহরণ মুছে ফেলতে পারে ,
আন্ডারস্কোর_

21

আপনাকে ওভাররাইডিং এবং ওভারলোডিংয়ের মধ্যে পার্থক্য করতে হবে। virtualকীওয়ার্ড ব্যতীত আপনি কেবল একটি বেস ক্লাসের একটি পদ্ধতি ওভারলোড করেন। এর অর্থ লুকোচুরি ছাড়া কিছুই নয়। ধরা যাক আপনার একটি বেস ক্লাস Baseএবং একটি উত্পন্ন শ্রেণি রয়েছে Specializedযা উভয়ই প্রয়োগ করে void foo()। এখন আপনার Baseউদাহরণের দিকে ইঙ্গিত করার জন্য একটি পয়েন্টার রয়েছে Specialized। আপনি যখন foo()এটির জন্য কল করবেন তখন আপনি এই পার্থক্যটি পর্যবেক্ষণ করতে পারবেন virtual: পদ্ধতিটি যদি ভার্চুয়াল হয় তবে প্রয়োগটি Specializedব্যবহার করা হবে, যদি এটি অনুপস্থিত থাকে তবে এর সংস্করণটি Baseবেছে নেওয়া হবে। বেস ক্লাস থেকে কখনই পদ্ধতিগুলি ওভারলোড না করা ভাল অনুশীলন। কোনও পদ্ধতিটিকে নন-ভার্চুয়াল তৈরি করা তার লেখকের উপায় যা আপনাকে জানাতে পারে যে এটি সাবক্লাসে এর সম্প্রসারণ উদ্দেশ্য নয়।


3
virtualআপনি ছাড়া ওভারলোডিং হয় না। তুমি ছায়া । একটি বেস বর্গ তাহলে Bএক বা একাধিক ফাংশন আছে foo, এবং উদ্ভূত বর্গ Dএকটি সংজ্ঞায়িত fooনাম, যে foo চামড়া ঐ সব fooমধ্যে -s BB::fooস্কোপ রেজুলেশন ব্যবহার করে তারা পৌঁছেছে । অতিরিক্ত লোড করার জন্য B::fooফাংশনগুলিকে প্রচার করতে D, আপনাকে ব্যবহার করতে হবে using B::foo
কাজ

20

আমাদের কেন সি ++ তে ভার্চুয়াল পদ্ধতি দরকার?

দ্রুত উত্তর:

  1. এটি আমাদের অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিংয়ের জন্য প্রয়োজনীয় "উপাদানগুলি" 1 সরবরাহ করে

বাজর্ন স্ট্রাস্ট্রাপ সি ++ প্রোগ্রামিংয়ে: নীতি ও অনুশীলন, (14.3):

ভার্চুয়াল ফাংশনটি বেস ক্লাসে একটি ফাংশন সংজ্ঞায়িত করার ক্ষমতা প্রদান করে এবং একই নামের একটি ফাংশন রাখে এবং ডাইরভেটিড ক্লাসে টাইপ করে যখন ব্যবহারকারী বেস ক্লাস ফাংশনটি কল করে। এটিকে প্রায়শই রান-টাইম পলিমারফিজম , ডায়নামিক প্রেরণ , বা রান-টাইম প্রেরণ বলা হয় কারণ ব্যবহৃত ফাংশনটি ব্যবহৃত বস্তুর ধরণের ভিত্তিতে রান টাইমে নির্ধারিত হয়।

  1. আপনার যদি ভার্চুয়াল ফাংশন কল 2 এর প্রয়োজন হয় এটি দ্রুততম কার্যকর বাস্তবায়ন ।

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


1. উত্তরাধিকার, রান-টাইম পলিমারফিজম এবং এনক্যাপসুলেশন ব্যবহার অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিংয়ের সর্বাধিক সাধারণ সংজ্ঞা ।

২. রান সময় সময়ে বিকল্পগুলির মধ্যে চয়ন করতে আপনি আরও দ্রুত হতে বা অন্যান্য ভাষার বৈশিষ্ট্য ব্যবহার করে কম মেমরি ব্যবহার করতে কার্যকারিতা কোড করতে পারবেন না। বাজনে স্ট্রস্ট্রপ সি ++ প্রোগ্রামিং: নীতি ও অনুশীলন (14.3.1)

৩. আমরা যখন ভার্চুয়াল ফাংশন সম্বলিত বেস ক্লাসটি কল করি তখন কোন ফাংশনটি সত্যই অনুরোধ করা হয় তা বলার জন্য কিছু।


15

একটি উত্তম পঠন হতে আমি কথোপকথনের আকারে আমার উত্তর পেয়েছি:


আমাদের ভার্চুয়াল ফাংশনগুলি কেন দরকার?

পলিমারফিজমের কারণে।

পলিমারফিজম কী?

একটি বেস পয়েন্টারটি উত্পন্ন ধরণের বস্তুগুলিকেও নির্দেশ করতে পারে।

পলিমারফিজমের এই সংজ্ঞাটি ভার্চুয়াল ফাংশনগুলির প্রয়োজনীয়তার দিকে পরিচালিত করে?

আচ্ছা, প্রথমদিকে বাঁধাইয়ের মাধ্যমে ।

তাড়াতাড়ি বাঁধাই কি?

সি ++ এ প্রারম্ভিক বাঁধাই (কম্পাইল-টাইম বাইন্ডিং) এর অর্থ প্রোগ্রামটি কার্যকর হওয়ার আগে একটি ফাংশন কল স্থির করা হয়।

তাই ...?

সুতরাং আপনি যদি কোনও ফাংশনের প্যারামিটার হিসাবে বেস টাইপটি ব্যবহার করেন তবে সংকলকটি কেবল বেস ইন্টারফেসটি স্বীকৃত করবে এবং আপনি যদি সেই ফাংশনটি উত্পন্ন শ্রেণি থেকে কোনও যুক্তি দিয়ে কল করেন তবে এটি কেটে ফেলা হবে, যা আপনি ঘটতে চান তা নয়।

যদি আমরা যা করতে চাই তা না হয় তবে কেন এটি অনুমোদিত?

কারণ আমাদের পলিমারফিজম দরকার!

পলিমারফিজমের সুবিধা কী?

আপনি একটি একক ফাংশনের প্যারামিটার হিসাবে বেস টাইপ পয়েন্টার ব্যবহার করতে পারেন এবং তারপরে আপনার প্রোগ্রামের রান-টাইমে আপনি কোনও একক ছাড়াই ডাইরফারেন্স ব্যবহার করে কোনও উদ্ভূত টাইপ ইন্টারফেসের (যেমন তাদের সদস্য ফাংশন) অ্যাক্সেস করতে পারবেন বেস পয়েন্টার

ভার্চুয়াল ফাংশনগুলি কীসের জন্য ভাল তা আমি এখনও জানি না ...! এবং এটি আমার প্রথম প্রশ্ন!

ঠিক আছে, কারণ আপনি খুব শীঘ্রই আপনার প্রশ্ন জিজ্ঞাসা করেছেন!

আমাদের ভার্চুয়াল ফাংশনগুলি কেন দরকার?

ধরে নিন যে আপনি একটি বেস পয়েন্টার সহ একটি ফাংশন বলেছেন, যার উত্পন্ন ক্লাসগুলির মধ্যে একটির সাথে কোনও বস্তুর ঠিকানা ছিল। যেমন আমরা উপরে এটি সম্পর্কে কথা বলেছি, রান-টাইমে, এই পয়েন্টারটি অবজ্ঞাপূর্ণ হয়ে যায়, এতদূর ভাল, তবে, আমরা আশা করি একটি পদ্ধতি (== একটি সদস্য ফাংশন) "আমাদের উত্পন্ন শ্রেণি থেকে" কার্যকর করা হবে! তবে, একই শ্রেণিটি (একই শিরোনামযুক্ত একটি) ইতিমধ্যে বেস শ্রেণিতে সংজ্ঞায়িত করা হয়েছে, সুতরাং আপনার প্রোগ্রামটি কেন অন্য পদ্ধতিটি বেছে নিতে বিরক্ত হবে? অন্য কথায় আমার অর্থ, আমরা সাধারণত এই ঘটনাটি দেখতে আগে যা করতে দেখতাম তা থেকে কীভাবে বলতে পারি?

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

আলাদা বাস্তবায়ন কেন?

তুমি মাথা নাক! একটি ভাল বই পড়তে যান !

ঠিক আছে, অপেক্ষা করুন অপেক্ষা করুন, কেন কেউ বেস পয়েন্টারগুলি ব্যবহার করতে বিরক্ত করবে, যখন সে কেবল ডাইরেক্টেড টাইপ পয়েন্টার ব্যবহার করতে পারে? আপনি বিচারক হন, এই সমস্ত মাথাব্যথা কি মূল্যবান? এই দুটি স্নিপেট দেখুন:

// 1:

Parent* p1 = &boy;
p1 -> task();
Parent* p2 = &girl;
p2 -> task();

// 2:

Boy* p1 = &boy;
p1 -> task();
Girl* p2 = &girl;
p2 -> task();

ঠিক আছে, যদিও আমি মনে করি 1 এখনও 2 এর চেয়ে ভাল তবে আপনি 1 টি এভাবে লিখতে পারেন:

// 1:

Parent* p1 = &boy;
p1 -> task();
p1 = &girl;
p1 -> task();

এবং তদ্ব্যতীত, আপনার সচেতন হওয়া উচিত যে এটি এখনও পর্যন্ত আমি আপনাকে যে সমস্ত বিষয় ব্যাখ্যা করেছি সেগুলির কেবলমাত্র একটি স্বীকৃত ব্যবহার। এর পরিবর্তে, উদাহরণস্বরূপ এমন একটি পরিস্থিতি ধরে নিন যাতে আপনি আপনার প্রোগ্রামে একটি ফাংশন রেখেছিলেন যা যথাক্রমে উত্পন্ন ক্লাসের প্রতিটি পদ্ধতি (getMonthBenefit ()) ব্যবহার করে:

double totalMonthBenefit = 0;    
std::vector<CentralShop*> mainShop = { &shop1, &shop2, &shop3, &shop4, &shop5, &shop6};
for(CentralShop* x : mainShop){
     totalMonthBenefit += x -> getMonthBenefit();
}

এখন, কোনও মাথাব্যথা ছাড়াই এটি আবার লেখার চেষ্টা করুন !

double totalMonthBenefit=0;
Shop1* branch1 = &shop1;
Shop2* branch2 = &shop2;
Shop3* branch3 = &shop3;
Shop4* branch4 = &shop4;
Shop5* branch5 = &shop5;
Shop6* branch6 = &shop6;
totalMonthBenefit += branch1 -> getMonthBenefit();
totalMonthBenefit += branch2 -> getMonthBenefit();
totalMonthBenefit += branch3 -> getMonthBenefit();
totalMonthBenefit += branch4 -> getMonthBenefit();
totalMonthBenefit += branch5 -> getMonthBenefit();
totalMonthBenefit += branch6 -> getMonthBenefit();

এবং প্রকৃতপক্ষে, এটি এখনও একটি স্বল্প উদাহরণ হতে পারে!


2
একক (অতি-) অবজেক্ট টাইপ ব্যবহার করে বিভিন্ন ধরণের (সাব) অবজেক্টগুলিতে পুনরাবৃত্তি করার ধারণাটি হাইলাইট করা উচিত, এটি একটি ভাল পয়েন্ট আপনি দিয়েছেন, ধন্যবাদ
কঠোরতা

14

যখন আপনার বেস ক্লাসে কোনও ফাংশন থাকে, আপনি ড্রেইড ক্লাসে Redefineবা Overrideএটি করতে পারেন ।

একটি পদ্ধতির পুনরায় সংজ্ঞা দেওয়া: উত্পন্ন শ্রেণিতে বেস শ্রেণির পদ্ধতির জন্য একটি নতুন বাস্তবায়ন দেওয়া হয়। সুবিধা দেয় নাDynamic binding

কোনও পদ্ধতিকে ওভাররাইড করে : উত্পন্ন শ্রেণিতে বেস শ্রেণীরRedefiningএকটিvirtual method। ভার্চুয়াল পদ্ধতি ডায়নামিক বাঁধাইয়ের সুবিধা দেয়

সুতরাং যখন আপনি বলেছেন:

তবে বইয়ের প্রথমদিকে, যখন বুনিয়াদি উত্তরাধিকার সম্পর্কে জানার সময়, আমি 'ভার্চুয়াল' ব্যবহার না করে উত্পন্ন শ্রেণিতে বেস পদ্ধতিগুলি ওভাররাইড করতে সক্ষম হয়েছি।

বেস ক্লাসের পদ্ধতিটি ভার্চুয়াল নয় বলে আপনি এটিকে ওভাররাইড করছেন না, বরং আপনি এটির নতুন সংজ্ঞা দিচ্ছেন


11

আপনি যদি অন্তর্নিহিত প্রক্রিয়াগুলি জানেন তবে এটি সহায়তা করে। সি ++ সি প্রোগ্রামারদের ব্যবহৃত কিছু কোডিং কৌশলকে আনুষ্ঠানিক করে, "ওভারলে" ব্যবহার করে "ক্লাস" প্রতিস্থাপন করে - সাধারণ শিরোনাম বিভাগগুলির সাথে স্ট্রাক্ট বিভিন্ন ধরণের অবজেক্টগুলি পরিচালনা করতে ব্যবহৃত হত তবে কিছু সাধারণ ডেটা বা ক্রিয়াকলাপ দ্বারা ব্যবহৃত হত। সাধারণত ওভারলে এর বেস স্ট্রাক্ট (সাধারণ অংশ) এর একটি ফাংশন টেবিলের জন্য একটি পয়েন্টার থাকে যা প্রতিটি বস্তুর ধরণের জন্য রুটিনের বিভিন্ন সেটকে নির্দেশ করে। সি ++ একই কাজ করে তবে প্রক্রিয়াগুলি লুকিয়ে রাখে যেমন সি ++ ptr->func(...)যেখানে ফানক সি হিসাবে ভার্চুয়াল হয় (*ptr->func_table[func_num])(ptr,...), যেখানে উদ্ভূত শ্রেণীর মধ্যে কী পরিবর্তন হয় তা ফান_ টেবিলের বিষয়বস্তু। [একটি অ-ভার্চুয়াল পদ্ধতি পিটিআর -> ফানক () স্রেফ মঙ্গলে_ফঙ্ক (পিটিআর, ..) এ অনুবাদ করে]

এর ফলশ্রুতিটি হ'ল ড্রেইড ক্লাসের পদ্ধতিগুলি কল করার জন্য আপনার কেবল বেস ক্লাসটি বুঝতে হবে, যদি কোনও রুটিন ক্লাস এ বুঝতে পারে তবে আপনি এটি একটি ডেরিভেড ক্লাস বি পয়েন্টারটি পাস করতে পারেন তবে ডাকা ভার্চুয়াল পদ্ধতিগুলি সেগুলি হবে বি এর পরিবর্তে এ এর ​​চেয়ে আপনি ফাংশন টেবিল বি পয়েন্টে যান through


8

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

এই লিঙ্কে আরও বিশদ http://cplusplusinterviews.blogspot.sg/2015/04/virtual-mechanism.html


7

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

Shape *shape = new Triangle(); 
cout << shape->getName();

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

ভার্চুয়াল টেবিল প্রক্রিয়া যা কম্পাইলার উপশ্রেণী বিভিন্ন ভার্চুয়াল-পদ্ধতি বাস্তবায়নের ট্র্যাক রাখে হয়। এটিকে ডায়নামিক প্রেরণও বলা হয় এবং রয়েছে কিছু এটির সাথে সম্পর্কযুক্ত ওভারহেড।

অবশেষে, কেন ভার্চুয়াল এমনকি সি ++ এও প্রয়োজন, কেন এটি জাভার মতো ডিফল্ট আচরণ করে না?

  1. সি ++ "জিরো ওভারহেড" এবং "আপনি যা ব্যবহার করেন তার জন্য অর্থ প্রদান করুন" নীতিগুলির উপর ভিত্তি করে। সুতরাং এটি আপনার জন্য গতিশীল প্রেরণের চেষ্টা করে না, যদি না এটির প্রয়োজন হয়।
  2. ইন্টারফেসে আরও নিয়ন্ত্রণ সরবরাহ করা। কোনও ফাংশন অ-ভার্চুয়াল তৈরি করে, ইন্টারফেস / বিমূর্ত শ্রেণি তার সমস্ত বাস্তবায়নে আচরণ নিয়ন্ত্রণ করতে পারে।

4

আমাদের ভার্চুয়াল ফাংশনগুলি কেন দরকার?

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

আসুন ভার্চুয়াল ফাংশনগুলির গুরুত্ব বোঝার জন্য দুটি সাধারণ প্রোগ্রামের নীচে তুলনা করা যাক:

ভার্চুয়াল ফাংশন ছাড়াই প্রোগ্রাম:

#include <iostream>
using namespace std;

class father
{
    public: void get_age() {cout << "Fathers age is 50 years" << endl;}
};

class son: public father
{
    public : void get_age() { cout << "son`s age is 26 years" << endl;}
};

int main(){
    father *p_father = new father;
    son *p_son = new son;

    p_father->get_age();
    p_father = p_son;
    p_father->get_age();
    p_son->get_age();
    return 0;
}

আউটপুট:

Fathers age is 50 years
Fathers age is 50 years
son`s age is 26 years

ভার্চুয়াল ফাংশন সহ প্রোগ্রাম:

#include <iostream>
using namespace std;

class father
{
    public:
        virtual void get_age() {cout << "Fathers age is 50 years" << endl;}
};

class son: public father
{
    public : void get_age() { cout << "son`s age is 26 years" << endl;}
};

int main(){
    father *p_father = new father;
    son *p_son = new son;

    p_father->get_age();
    p_father = p_son;
    p_father->get_age();
    p_son->get_age();
    return 0;
}

আউটপুট:

Fathers age is 50 years
son`s age is 26 years
son`s age is 26 years

উভয় আউটপুটগুলি ঘনিষ্ঠভাবে বিশ্লেষণ করে ভার্চুয়াল ফাংশনগুলির গুরুত্ব বুঝতে পারে।


3

ওওপি উত্তর: সাব টাইপ পলিমারফিজম

C ++, ভার্চুয়াল পদ্ধতি বুঝতে প্রয়োজন হয় পলিমরফিজম আরো সঠিকভাবে subtyping বা উপপ্রকার পলিমরফিজম যদি আপনি উইকিপিডিয়া থেকে সংজ্ঞা প্রযোজ্য।

উইকিপিডিয়া, সাব টাইপিং, 2019-01-09: প্রোগ্রামিং ল্যাঙ্গুয়েজ থিয়োরিয়ায় সাব টাইপিং (সাব-টাইপ পলিমারফিজম বা ইনক্লুশন পলিমারফিজম) এমন একধরণের পলিমারফিজম যেখানে একটি সাব টাইপ একটি ডেটাটাইপ যা অন্য কোনও ডেটাটাইপ (সুপারটাইপ) এর সাথে কিছু ধারণা দ্বারা সম্পর্কিত সাবস্টিটিউটিবিলিটির অর্থ, সুপারটাইপের উপাদানগুলিতে পরিচালিত লিখিত প্রোগ্রাম উপাদানগুলি, সাধারণত সাবরুটাইন বা ফাংশনগুলিও সাব টাইপের উপাদানগুলিতে কাজ করতে পারে।

দ্রষ্টব্য: সাবটাইপ মানে বেস ক্লাস, এবং সাব টাইপ মানে উত্তরাধিকার সূত্রে প্রাপ্ত শ্রেণি।

সাব টাইপ পলিমারফিজম সম্পর্কিত আরও পড়া

প্রযুক্তিগত উত্তর: গতিশীল প্রেরণ atch

যদি আপনার কোনও বেস ক্লাসের পয়েন্টার থাকে, তবে পদ্ধতির কলটি (এটি ভার্চুয়াল হিসাবে ঘোষিত) তৈরি বস্তুর প্রকৃত শ্রেণির পদ্ধতিতে প্রেরণ করা হবে। এভাবেই সাব টাইপ পলিমারফিজমটি উপলব্ধি করা হয় সি ++।

C ++ এবং ডায়নামিক ডিসপ্যাচে আরও বহুবচন পড়া

বাস্তবায়ন উত্তর: vtable এন্ট্রি তৈরি করে

পদ্ধতিগুলিতে প্রতিটি সংশোধক "ভার্চুয়াল" এর জন্য, সি ++ সংকলকরা সাধারণত ক্লাসের ভিটিবেলে একটি পদ্ধতি তৈরি করে যেখানে পদ্ধতিটি ঘোষিত হয়। সাধারণ সি ++ সংকলক ডায়নামিক ডিসপ্যাচটি এইভাবে উপলব্ধি করে ।

আরও পড়া vtables


উদাহরণ কোড

#include <iostream>

using namespace std;

class Animal {
public:
    virtual void MakeTypicalNoise() = 0; // no implementation needed, for abstract classes
    virtual ~Animal(){};
};

class Cat : public Animal {
public:
    virtual void MakeTypicalNoise()
    {
        cout << "Meow!" << endl;
    }
};

class Dog : public Animal {
public:
    virtual void MakeTypicalNoise() { // needs to be virtual, if subtype polymorphism is also needed for Dogs
        cout << "Woof!" << endl;
    }
};

class Doberman : public Dog {
public:
    virtual void MakeTypicalNoise() {
        cout << "Woo, woo, woow!";
        cout << " ... ";
        Dog::MakeTypicalNoise();
    }
};

int main() {

    Animal* apObject[] = { new Cat(), new Dog(), new Doberman() };

    const   int cnAnimals = sizeof(apObject)/sizeof(Animal*);
    for ( int i = 0; i < cnAnimals; i++ ) {
        apObject[i]->MakeTypicalNoise();
    }
    for ( int i = 0; i < cnAnimals; i++ ) {
        delete apObject[i];
    }
    return 0;
}

উদাহরণ কোডের আউটপুট

Meow!
Woof!
Woo, woo, woow! ... Woof!

কোড উদাহরণের ইউএমএল বর্গ চিত্র

কোড উদাহরণের ইউএমএল বর্গ চিত্র


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

2

ভার্চুয়াল পদ্ধতিটি কেন ব্যবহৃত হয় তা চিত্রিত করে এমন সম্পূর্ণ উদাহরণ এখানে।

#include <iostream>

using namespace std;

class Basic
{
    public:
    virtual void Test1()
    {
        cout << "Test1 from Basic." << endl;
    }
    virtual ~Basic(){};
};
class VariantA : public Basic
{
    public:
    void Test1()
    {
        cout << "Test1 from VariantA." << endl;
    }
};
class VariantB : public Basic
{
    public:
    void Test1()
    {
        cout << "Test1 from VariantB." << endl;
    }
};

int main()
{
    Basic *object;
    VariantA *vobjectA = new VariantA();
    VariantB *vobjectB = new VariantB();

    object=(Basic *) vobjectA;
    object->Test1();

    object=(Basic *) vobjectB;
    object->Test1();

    delete vobjectA;
    delete vobjectB;
    return 0;
}

1

দক্ষতা সম্পর্কে, ভার্চুয়াল ফাংশনগুলি প্রাথমিক-বাঁধাই করা কার্যকারিতা হিসাবে কিছুটা কম দক্ষ।

"এই ভার্চুয়াল কল প্রক্রিয়াটি" সাধারণ ফাংশন কল "মেকানিজমের (প্রায় 25% এর মধ্যে) প্রায় দক্ষ তৈরি করা যায় Its একজন সিউর ট্যুর + ব্জার্ন স্ট্রস্ট্রপ]


2
দেরীতে বাইন্ডিং কেবল ফাংশন কলকে ধীর করে দেয় না, এটি রান সময় পর্যন্ত কলটিকে অজানা করে তোলে, সুতরাং ফাংশন কল জুড়ে অপ্টিমাইজেশন প্রয়োগ করা যায় না। এটি f.ex. এমন ক্ষেত্রে যেখানে মান প্রচারগুলি প্রচুর কোড সরিয়ে দেয় (ভেবে দেখুন if(param1>param2) return cst;যেখানে কম্পাইলার কিছু ক্ষেত্রে স্থিরভাবে পুরো ফাংশন কলকে হ্রাস করতে পারে)।
কৌতূহলী

1

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

interface IUnknown {
  virtual HRESULT QueryInterface (REFIID riid, void **ppvObject) = 0;
  virtual ULONG   AddRef () = 0;
  virtual ULONG   Release () = 0;
};

এই পদ্ধতিগুলি প্রয়োগ করার জন্য ইন্টারফেস ব্যবহারকারীর কাছে রেখে দেওয়া হয়। এগুলি নির্দিষ্ট অবজেক্ট তৈরি এবং ধ্বংসের জন্য প্রয়োজনীয় যা IUnعلوم এর উত্তরাধিকারী হতে হবে। এই ক্ষেত্রে রান-টাইম তিনটি পদ্ধতি সম্পর্কে অবগত এবং এটি যখন তাদের আহ্বান জানায় তখন এগুলি বাস্তবায়িত হবে বলে আশা করে। সুতরাং এক অর্থে তারা বস্তু নিজেই এবং যে জিনিসটি ব্যবহার করে তার মধ্যে একটি চুক্তি হিসাবে কাজ করে।


the run-time is aware of the three methods and expects them to be implementedতারা বিশুদ্ধ ভার্চুয়াল আছেন, সেখানে একটি দৃষ্টান্ত তৈরি করতে কোন উপায় নেই IUnknown, এবং তাই সব উপশ্রেণী আবশ্যক করার জন্য এই ধরনের সব পদ্ধতি বাস্তবায়ন নিছক কম্পাইল। এগুলি বাস্তবায়ন না করার এবং কেবল রানটাইমের সময় এটি খুঁজে পাওয়ার কোনও বিপদ নেই (তবে স্পষ্টতই কেউ এগুলি অবশ্যই ভুলভাবে প্রয়োগ করতে পারে !)। এবং বাহ, আজ আমি #defineএই শব্দটি সহ উইন্ডোজ সা-ম্যাক্রো শিখেছি interfaceসম্ভবত: কারণ তাদের ব্যবহারকারীরা কেবলমাত্র (এ) Iনামের উপসর্গ দেখতে পারে না বা (বি) ক্লাসে এটি দেখতে একটি ইন্টারফেস দেখতে পারে না। উঘ
আন্ডারস্কোর_২০

1

আমি মনে করি আপনি একবার পদ্ধতির ভার্চুয়াল ঘোষণার পরে আপনি সেই সত্যটির উল্লেখ করছেন আপনার ওভাররাইডে 'ভার্চুয়াল' কীওয়ার্ডটি ব্যবহার করার দরকার নেই।

class Base { virtual void foo(); };

class Derived : Base 
{ 
  void foo(); // this is overriding Base::foo
};

আপনি যদি বেসের ফু-ডিক্লোরেশনে 'ভার্চুয়াল' ব্যবহার না করেন তবে ডেরিভের ফু কেবল এটিকে ছায়া দিবে।


1

প্রথম দুটি উত্তরের জন্য এখানে সি ++ কোডের মার্জ করা সংস্করণ is

#include        <iostream>
#include        <string>

using   namespace       std;

class   Animal
{
        public:
#ifdef  VIRTUAL
                virtual string  says()  {       return  "??";   }
#else
                string  says()  {       return  "??";   }
#endif
};

class   Dog:    public Animal
{
        public:
                string  says()  {       return  "woof"; }
};

string  func(Animal *a)
{
        return  a->says();
}

int     main()
{
        Animal  *a = new Animal();
        Dog     *d = new Dog();
        Animal  *ad = d;

        cout << "Animal a says\t\t" << a->says() << endl;
        cout << "Dog d says\t\t" << d->says() << endl;
        cout << "Animal dog ad says\t" << ad->says() << endl;

        cout << "func(a) :\t\t" <<      func(a) <<      endl;
        cout << "func(d) :\t\t" <<      func(d) <<      endl;
        cout << "func(ad):\t\t" <<      func(ad)<<      endl;
}

দুটি পৃথক ফলাফল:

# ডেফাইন ভার্চুয়াল ছাড়া এটি সংকলন সময়ে আবদ্ধ হয়। অ্যানিমাল * বিজ্ঞাপন এবং ফানক (অ্যানিমাল *) সমস্ত পশুর হ'ল () পদ্ধতির দিকে ইঙ্গিত করে।

$ g++ virtual.cpp -o virtual
$ ./virtual 
Animal a says       ??
Dog d says      woof
Animal dog ad says  ??
func(a) :       ??
func(d) :       ??
func(ad):       ??

# ডেফাইন ভার্চুয়াল সহ এটি রান টাইমে বাঁধা থাকে। কুকুর * ডি, পশুর * বিজ্ঞাপন এবং ফানক (প্রাণী *) পয়েন্ট / কুকুরের কথায় () পদ্ধতিটি উল্লেখ করুন কারণ কুকুর তাদের বস্তুর ধরণ। [কুকুরের () "ওয়ুফ"] পদ্ধতিটি সংজ্ঞায়িত না করা না হলে শ্রেণি গাছের মধ্যে এটি প্রথম অনুসন্ধান করা হবে, অর্থাত্ উদ্ভূত শ্রেণিগুলি তাদের বেস শ্রেণীর পদ্ধতিগুলিকে ওভাররাইড করতে পারে [অ্যানিম্যালস বলে ()]।

$ g++ virtual.cpp -D VIRTUAL -o virtual
$ ./virtual 
Animal a says       ??
Dog d says      woof
Animal dog ad says  woof
func(a) :       ??
func(d) :       woof
func(ad):       woof

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

class   Animal:
        def     says(self):
                return  "??"

class   Dog(Animal):
        def     says(self):
                return  "woof"

def     func(a):
        return  a.says()

if      __name__ == "__main__":

        a = Animal()
        d = Dog()
        ad = d  #       dynamic typing by assignment

        print("Animal a says\t\t{}".format(a.says()))
        print("Dog d says\t\t{}".format(d.says()))
        print("Animal dog ad says\t{}".format(ad.says()))

        print("func(a) :\t\t{}".format(func(a)))
        print("func(d) :\t\t{}".format(func(d)))
        print("func(ad):\t\t{}".format(func(ad)))

আউটপুটটি হ'ল:

Animal a says       ??
Dog d says      woof
Animal dog ad says  woof
func(a) :       ??
func(d) :       woof
func(ad):       woof

যা সি ++ এর ভার্চুয়াল সংজ্ঞায়িত অনুরূপ। নোট করুন যে ডি এবং বিজ্ঞাপন দুটি ভিন্ন পয়েন্টার ভেরিয়েবল একই ডগ ইভেন্টে উল্লেখ / নির্দেশ করছে। এক্সপ্রেশন (বিজ্ঞাপনটি ডি) সত্য এবং তাদের মানগুলি একই < মূল। 0xb79f72cc এ ডগ অবজেক্ট> returns


0

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


0

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


-1

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

 class base {
 public:
 void helloWorld() { std::cout << "Hello World!"; }
  };

 class derived: public base {
 public:
 void helloWorld() { std::cout << "Greetings World!"; }
 };

 int main () {
      base hwOne;
      derived hwTwo = new derived();
      base->helloWorld(); //prints "Hello World!"
      derived->helloWorld(); //prints "Hello World!"

ঠিক আছে, তাই আমরা জানি যে। এখন এটি সদস্য-ফাংশন পয়েন্টারগুলির সাথে করার চেষ্টা করি:

 #include <iostream>
 using namespace std;

 class base {
 public:
 void helloWorld() { std::cout << "Hello World!"; }
 };

 class derived : public base {
 public:
 void displayHWDerived(void(derived::*hwbase)()) { (this->*hwbase)(); }
 void(derived::*hwBase)();
 void helloWorld() { std::cout << "Greetings World!"; }
 };

 int main()
 {
 base* b = new base(); //Create base object
 b->helloWorld(); // Hello World!
 void(derived::*hwBase)() = &derived::helloWorld; //create derived member 
 function pointer to base function
 derived* d = new derived(); //Create derived object. 
 d->displayHWDerived(hwBase); //Greetings World!

 char ch;
 cin >> ch;
 }

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

অন্যদিকে, ভার্চুয়াল ফাংশনগুলি যখন তাদের কিছু ফাংশন-পয়েন্টার ওভারহেড থাকতে পারে, তখন নাটকীয়ভাবে জিনিসগুলি সরল করুন।

সম্পাদনা: আর একটি পদ্ধতি রয়েছে যা এডিডিট্রির অনুরূপ: সি ++ ভার্চুয়াল ফাংশন বনাম সদস্য ফাংশন পয়েন্টার (পারফরম্যান্স তুলনা)

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