আমাদের কেন সি ++ এ খাঁটি ভার্চুয়াল ডেস্ট্রাক্টর দরকার?


154

আমি ভার্চুয়াল ডেস্ট্রাক্টরের প্রয়োজনীয়তা বুঝতে পারি। তবে কেন আমাদের খাঁটি ভার্চুয়াল ডেস্ট্রাক্টর দরকার? সি +++ এর একটি নিবন্ধে লেখক উল্লেখ করেছেন যে আমরা যখন ক্লাস বিমূর্ত করতে চাই তখন আমরা খাঁটি ভার্চুয়াল ডেস্ট্রাক্টর ব্যবহার করি।

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

আমার প্রশ্ন তাই হয়

  1. আমরা কখন একজন ডেস্ট্রাক্টরকে খাঁটি ভার্চুয়াল করি? কেউ কি একটি ভাল বাস্তব সময়ের উদাহরণ দিতে পারে?

  2. আমরা যখন বিমূর্ত ক্লাস তৈরি করছি তখন ডেস্ট্রাক্টরকেও খাঁটি ভার্চুয়াল বানানো কি ভাল অনুশীলন? যদি হ্যাঁ .. তবে কেন?


4
একাধিক প্রতিলিপি: stackoverflow.com/questions/999340/... এবং stackoverflow.com/questions/630950/pure-virtual-destructor-in-c হচ্ছে তাদের দুটি
ড্যানিয়েল Sloof

14
@ ড্যানিয়েল- উল্লিখিত লিঙ্কগুলি আমার প্রশ্নের উত্তর দেয় না। খাঁটি ভার্চুয়াল ডেস্ট্রাক্টরের একটি সংজ্ঞা থাকতে হবে কেন এটি এর উত্তর দেয়। আমার প্রশ্ন হ'ল কেন আমাদের খাঁটি ভার্চুয়াল ডেস্ট্রাক্টর দরকার।
চিহ্নিত করুন

আমি কারণটি অনুসন্ধান করার চেষ্টা করছিলাম তবে আপনি ইতিমধ্যে এখানে প্রশ্নটি করেছিলেন।
nsivakr

উত্তর:


119
  1. খাঁটি ভার্চুয়াল ডেস্ট্রাক্টরদের অনুমোদিত হওয়ার সম্ভবত আসল কারণ হ'ল তাদের নিষিদ্ধ করার অর্থ ভাষাতে আরও একটি বিধি যুক্ত করা এবং এই নিয়মের কোনও প্রয়োজন নেই কারণ খাঁটি ভার্চুয়াল ডেস্ট্রাক্টরকে অনুমতি দেওয়ার ফলে কোনও খারাপ প্রভাব আসতে পারে না।

  2. নাহ, সরল পুরাতন ভার্চুয়াল যথেষ্ট।

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

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

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

নোট: বিনাশকারী শুধুমাত্র পদ্ধতি যদিও তা যে হয় বিশুদ্ধ ভার্চুয়াল হয়েছে instantiate উদ্ভূত করার শ্রেণীর (হ্যাঁ বিশুদ্ধ ভার্চুয়াল ফাংশন বাস্তবায়নের থাকতে পারে) অনুক্রমে একটি বাস্তবায়ন আছে।

struct foo {
    virtual void bar() = 0;
};

void foo::bar() { /* default implementation */ }

class foof : public foo {
    void bar() { foo::bar(); } // have to explicitly call default implementation.
};

13
"হ্যাঁ খাঁটি ভার্চুয়াল ফাংশনগুলির প্রয়োগ থাকতে পারে" তারপরে এটি খাঁটি ভার্চুয়াল নয়।
GManNickG

2
আপনি যদি কোনও শ্রেণি বিমূর্ত করতে চান, তবে কেবল সমস্ত নির্মাণকারীকে সুরক্ষিত করা কি সহজ হবে না?
বিডনলান

78
@ জিএমান, আপনি ভুল করে বলেছেন, খাঁটি ভার্চুয়াল মানে ডেরিভড ক্লাসগুলি অবশ্যই এই পদ্ধতিটিকে ওভাররাইড করতে হবে, এটি বাস্তবায়নের ক্ষেত্রে গোঁড়া। আমার কোডটি দেখুন এবং foof::barযদি আপনি নিজের জন্য দেখতে চান তবে মন্তব্য করুন।
মোটি

15
@ জিএমএন: সি ++ এফএকিউ লাইট বলেছেন "নোট করুন যে খাঁটি ভার্চুয়াল ফাংশনটির জন্য একটি সংজ্ঞা প্রদান করা সম্ভব, তবে এটি সাধারণত নবজাতককে বিভ্রান্ত করে এবং পরে অবধি সবচেয়ে ভাল এড়ানো যায়।" parashift.com/c++-faq-lite/abcs.html#faq-22.4 উইকিপিডিয়া (যা সঠিকতার ঘাঁটি) একইভাবে বলেছে। আমি বিশ্বাস করি যে আইএসও / আইইসি স্ট্যান্ডার্ড একই ধরণের পরিভাষা ব্যবহার করে (দুর্ভাগ্যক্রমে আমার অনুলিপিটি এই মুহুর্তে কাজ করছে) ... আমি সম্মত হই যে এটি বিভ্রান্তিকর, এবং আমি সাধারণত স্পষ্টতা ছাড়াই শব্দটি ব্যবহার করি না যখন আমি কোনও সংজ্ঞা দিচ্ছি, বিশেষত নতুন প্রোগ্রামারগুলির আশেপাশে ...

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

33

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


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

4
@ সুরফিং: কারণ একটি উদ্ভূত শ্রেণীর একজন ডেস্ট্রাক্টর স্পষ্টভাবে তার বেস বর্গের ডেস্ট্রাক্টরকে কল করে, এমনকি যদি সেই ডেস্ট্রাক্টর খাঁটি ভার্চুয়াল হয়। সুতরাং এটির জন্য যদি কোনও বাস্তবায়ন না হয় তবে অপরিজ্ঞাত বাহাওয়ারটি ঘটতে চলেছে।
a.peganz

19

আপনি যদি একটি বিমূর্ত বেস শ্রেণি তৈরি করতে চান:

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

... ডিস্ট্রাক্টরকে খাঁটি ভার্চুয়াল তৈরি করে এবং এর জন্য একটি সংজ্ঞা (পদ্ধতি সংস্থা) সরবরাহ করে শ্রেণি বিমূর্ত করা সহজ iest

আমাদের হাইপোটিক্যাল এবিসি:

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


8

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

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

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

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

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

struct IParams
{
    IParams(const ModelConfiguration& aModelConf);
    virtual ~IParams() = 0;

    void setParameter(const N_Configuration::Parameter& aParam);

    std::map<std::string, std::string> m_Parameters;
};

struct NumericsParams : IParams
{
    NumericsParams(const ModelConfiguration& aNumericsConf);
    virtual ~NumericsParams();

    double dt() const;
    double ti() const;
    double tf() const;
};

struct PhysicsParams : IParams
{
    PhysicsParams(const N_Configuration::ModelConfiguration& aPhysicsConf);
    virtual ~PhysicsParams();

    double g()     const; 
    double rho_i() const; 
    double rho_w() const; 
};

1
আমি এই ব্যবহারটি পছন্দ করি, তবে উত্তরাধিকার "প্রয়োগ" করার আরেকটি উপায় হ'ল নির্ধারককে IParamসুরক্ষিত ঘোষণা করার মাধ্যমে , অন্য কোনও মন্তব্যে যেমন উল্লেখ করা হয়েছিল।
rwols

4

আপনি যদি ইতিমধ্যে বাস্তবায়িত এবং পরীক্ষিত ডেরিভ ক্লাসে কোনও পরিবর্তন না করে বেস ক্লাসের ইনস্ট্যান্টেশন বন্ধ করতে চান, তবে আপনি আপনার বেস ক্লাসে খাঁটি ভার্চুয়াল ডেস্ট্রাক্টর প্রয়োগ করেন।


3

এখানে আমি বলতে চাই কখন আমাদের ভার্চুয়াল ডেস্ট্রাক্টর প্রয়োজন এবং কখন আমাদের খাঁটি ভার্চুয়াল ডেস্ট্রাক্টর দরকার

class Base
{
public:
    Base();
    virtual ~Base() = 0; // Pure virtual, now no one can create the Base Object directly 
};

Base::Base() { cout << "Base Constructor" << endl; }
Base::~Base() { cout << "Base Destructor" << endl; }


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

Derived::Derived() { cout << "Derived Constructor" << endl; }
Derived::~Derived() {   cout << "Derived Destructor" << endl; }


int _tmain(int argc, _TCHAR* argv[])
{
    Base* pBase = new Derived();
    delete pBase;

    Base* pBase2 = new Base(); // Error 1   error C2259: 'Base' : cannot instantiate abstract class
}
  1. আপনি যখন চান যে কেউ বেস ক্লাসের অবজেক্টটি সরাসরি তৈরি করতে সক্ষম না হয়, খাঁটি ভার্চুয়াল ডেস্ট্রাক্টর ব্যবহার করুন virtual ~Base() = 0। সাধারণত কমপক্ষে একটি খাঁটি ভার্চুয়াল ফাংশন প্রয়োজন, virtual ~Base() = 0এই ফাংশন হিসাবে নেওয়া যাক ।

  2. যখন আপনার উপরের জিনিসটির প্রয়োজন হবে না, কেবলমাত্র আপনার ডারাইভড শ্রেণীর অবজেক্টের নিরাপদ ধ্বংসের প্রয়োজন

    বেস * pBase = নতুন উত্পন্ন (); পিবেস মুছুন; খাঁটি ভার্চুয়াল ডেস্ট্রাক্টর প্রয়োজন হয় না, কেবল ভার্চুয়াল ডেস্ট্রাক্টরই কাজটি করবে।


2

আপনি এই উত্তরগুলির সাথে হাইপোপটিকালগুলিতে পড়ছেন, সুতরাং আমি স্বচ্ছতার জন্য পৃথিবীর ব্যাখ্যাকে আরও সহজ করে তুলতে চেষ্টা করব।

অবজেক্ট ওরিয়েন্টেড ডিজাইনের প্রাথমিক সম্পর্ক দুটি: IS-A এবং HAS-A। আমি সেগুলি তৈরি করিনি। এগুলিই তাদের বলা হয়।

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

একটি-ইঙ্গিত করেছে যে কোনও বস্তু একটি সম্মিলিত শ্রেণীর অংশ এবং এর মালিকানার সম্পর্ক রয়েছে। সি ++ এর অর্থ এটি একটি সদস্য অবজেক্ট এবং এরূপ অনূদিত এটির নিষ্পত্তি করার জন্য নিজস্ব শ্রেণিতে থাকে বা নিজেকে ধ্বংস করার আগে মালিকানা বন্ধ করে দেয়।

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

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

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

কোনও ফলের শ্রেণিতে ভার্চুয়াল ফাংশন রঙ () থাকতে পারে যা ডিফল্টরূপে "NONE" ফেরত দেয়। কলা শ্রেণির রঙ () ফাংশনটি "ইয়েলো" বা "ব্রাউন" দেয়।

তবে যদি কোনও ফলের পয়েন্টার গ্রহণকারী ফাংশনটি কলটি পাঠানো কলা শ্রেণিতে রঙ () কল করে - কোন রঙ () ফাংশনটি চাওয়া হয়? ফাংশনটি সাধারণত একটি ফলের অবজেক্টের জন্য ফল :: রঙ () বলে would

এটি হবে 99% সময় যা ছিল তার উদ্দেশ্য ছিল না। তবে যদি ফল :: রঙ () ভার্চুয়াল হিসাবে ঘোষিত হয় তবে কলা: রঙ () অবজেক্টের জন্য কল করা হবে কারণ কল করার সময় সঠিক রঙ () ফাংশনটি ফলের পয়েন্টারে আবদ্ধ থাকবে। রানটাইম পরীক্ষা করবে যে পয়েন্টারটি কীসের দিকে ইঙ্গিত করে কারণ এটি ফল শ্রেণীর সংজ্ঞাতে ভার্চুয়াল হিসাবে চিহ্নিত হয়েছিল।

এটি একটি সাবক্লাসে কোনও ক্রিয়াকলাপকে ওভাররাইড করার চেয়ে পৃথক। সেক্ষেত্রে ফলের পয়েন্টার ফলটিকে :: :: কল (কল) কল করবে যদি সমস্ত কিছু জানা থাকে তবে তা হ'ল ফলের কাছে এটি পয়েন্টার ter

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

ধাঁধাটির এখন চূড়ান্ত টুকরো: নির্মাণকারী এবং ধ্বংসকারী।

খাঁটি ভার্চুয়াল নির্মাতারা সম্পূর্ণ অবৈধ। এটা ঠিক বাইরে।

খাঁটি ভার্চুয়াল ডেস্ট্রাক্টররা সেই ক্ষেত্রে কাজ করে যা আপনি একটি বেস শ্রেণীর উদাহরণ তৈরি করতে নিষেধ করতে চান। বেস ক্লাসের ডেস্ট্রাক্টর খাঁটি ভার্চুয়াল হলে কেবল সাব ক্লাসই ইনস্ট্যান্ট করা যায়। কনভেনশনটি 0 তে নির্ধারিত হয়।

 virtual ~Fruit() = 0;  // pure virtual 
 Fruit::~Fruit(){}      // destructor implementation

এই ক্ষেত্রে আপনাকে একটি বাস্তবায়ন তৈরি করতে হবে। সংকলকটি জানে যে এটি আপনি যা করছেন এবং এটি সঠিকভাবে করছেন তা নিশ্চিত করে তোলে, বা এটি দৃ or়তার সাথে অভিযোগ করে যে এটি সংকলনের জন্য প্রয়োজনীয় সমস্ত কার্যগুলির সাথে লিঙ্ক করতে পারে না। আপনি কীভাবে আপনার শ্রেণীর শ্রেণিবিন্যাসের মডেলিং করছেন সে সম্পর্কে সঠিক পথে না থাকলে ত্রুটিগুলি বিভ্রান্তিকর হতে পারে।

সুতরাং আপনাকে এই ক্ষেত্রে ফলের উদাহরণ তৈরি করতে নিষেধ করা হয়েছে, তবে কলা উদাহরণ তৈরি করার অনুমতি দেওয়া হয়েছে।

ফলের পয়েন্টারটিকে মুছার জন্য যে কলটি কলার উদাহরণ দেখায় তাতে কলার কলকে :: :: কলা () প্রথমে কল করা হবে এবং তারপরে ফাইটকে :: always ফল () বলা হবে, সর্বদা। কারণ যাই হোক না কেন, আপনি যখন সাবক্লাস ডেস্ট্রাক্টর কল করেন তখন বেস ক্লাসের ডেস্ট্রাক্টরকে অবশ্যই অনুসরণ করতে হবে।

এটা কি খারাপ মডেল? এটি নকশার পর্যায়ে আরও জটিল, হ্যাঁ, তবে এটি নিশ্চিত করতে পারে যে রান লিঙ্কে সঠিক লিঙ্কিং সম্পাদিত হয়েছে এবং একটি সাবক্লাস ফাংশন সম্পাদিত হয়েছে যেখানে ঠিক কোন সাবক্লাসটি অ্যাক্সেস করা হচ্ছে তা নিয়ে দ্বিধা রয়েছে।

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

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


1

এটি এক দশকের পুরানো বিষয় :) বিশদ বিবরণের জন্য "কার্যকর সি ++" বইয়ের আইটেম # 7 এর শেষ 5 অনুচ্ছেদটি পড়ুন, থেকে শুরু হয় "মাঝে মাঝে ক্লাসকে খাঁটি ভার্চুয়াল ডেস্ট্রাক্টর দেওয়া সুবিধাজনক হতে পারে ...."


0

আপনি একটি উদাহরণ চেয়েছিলেন, এবং আমি বিশ্বাস করি যে নিম্নলিখিতটি খাঁটি ভার্চুয়াল ডেস্ট্রাক্টরের একটি কারণ সরবরাহ করে। আমি উত্তরের জন্য অপেক্ষা করছি এটি একটি ভাল কারণ কিনা ...

আমি চাই না যে কেউ error_baseটাইপটি ছুঁড়ে ফেলতে সক্ষম হোক , তবে ব্যতিক্রমের ধরণের error_oh_shucksএবং error_oh_blastএকই ধরণের কার্যকারিতা রয়েছে এবং আমি এটি দুবার লিখতে চাই না। std::stringআমার ক্লায়েন্টদের সংস্পর্শ এড়াতে পিআইএমপিএল জটিলতা std::auto_ptrপ্রয়োজনীয় এবং অনুলিপি নির্মাণকারীর ব্যবহার প্রয়োজন।

পাবলিক শিরোনামটিতে ব্যতিক্রমের স্পেসিফিকেশন রয়েছে যা ক্লায়েন্টের কাছে আমার লাইব্রেরি দ্বারা ছড়িয়ে দেওয়া বিভিন্ন ধরণের ব্যতিক্রমের পার্থক্য করার জন্য উপলব্ধ থাকবে:

// error.h

#include <exception>
#include <memory>

class exception_string;

class error_base : public std::exception {
 public:
  error_base(const char* error_message);
  error_base(const error_base& other);
  virtual ~error_base() = 0; // Not directly usable

  virtual const char* what() const;
 private:
  std::auto_ptr<exception_string> error_message_;
};

template<class error_type>
class error : public error_base {
 public:
   error(const char* error_message) : error_base(error_message) {}
   error(const error& other) : error_base(other) {}
   ~error() {}
};

// Neither should these classes be usable
class error_oh_shucks { virtual ~error_oh_shucks() = 0; }
class error_oh_blast { virtual ~error_oh_blast() = 0; }

এবং এখানে ভাগ করা বাস্তবায়ন:

// error.cpp

#include "error.h"
#include "exception_string.h"

error_base::error_base(const char* error_message)
  : error_message_(new exception_string(error_message)) {}

error_base::error_base(const error_base& other)
  : error_message_(new exception_string(other.error_message_->get())) {}

error_base::~error_base() {}

const char* error_base::what() const {
  return error_message_->get();
}

ব্যাক্তিগত স্ট্রিং ক্লাসটি প্রাইভেট রাখা, আমার পাবলিক ইন্টারফেস থেকে স্ট্যান্ড :: স্ট্রিং লুকিয়ে রাখে:

// exception_string.h

#include <string>

class exception_string {
 public:
  exception_string(const char* message) : message_(message) {}

  const char* get() const { return message_.c_str(); }
 private:
  std::string message_;
};

আমার কোডটি তখন ত্রুটিটি নিক্ষেপ করে:

#include "error.h"

throw error<error_oh_shucks>("That didn't work");

এর জন্য একটি টেম্পলেট ব্যবহার errorকিছুটা কৃতজ্ঞ। এটি ক্লায়েন্টদের ত্রুটিগুলি ধরার জন্য প্রয়োজনীয় ব্যয় করে কিছুটা কোড সংরক্ষণ করে:

// client.cpp

#include <error.h>

try {
} catch (const error<error_oh_shucks>&) {
} catch (const error<error_oh_blast>&) {
}

0

খাঁটি ভার্চুয়াল ডেস্ট্রাক্টরের আরও একটি বাস্তব ব্যবহার-পরিস্থিতি রয়েছে যা আমি অন্য জবাবগুলিতে দেখতে পাচ্ছি না :)

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

প্রথমে এটি কল্পনা করুন:

class Printable {
  virtual void print() const = 0;
  // virtual destructor should be here, but not to confuse with another problem
};

এবং এর মতো কিছু:

class Printer {
  void queDocument(unique_ptr<Printable> doc);
  void printAll();
};

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

এবং এখন ঠিক একই ধারণাটি ব্যতীত এটি মুদ্রণের জন্য নয় ধ্বংসের জন্য:

class Destroyable {
  virtual ~Destroyable() = 0;
};

এবং একই ধরণের পাত্রে থাকতে পারে:

class PostponedDestructor {
  // Queues an object to be destroyed later.
  void queObjectForDestruction(unique_ptr<Destroyable> obj);
  // Destroys all already queued objects.
  void destroyAll();
};

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

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


-2

1) আপনি যখন ক্লিয়ার-আপ করার জন্য উত্পন্ন ক্লাসগুলির প্রয়োজন চান। এটি বিরল।

2) না, তবে আপনি এটি ভার্চুয়াল হতে চান, যদিও।


-2

আমাদের এটিকে ডিস্ট্রাক্টর ভার্চুয়াল ব্যাকস তৈরি করতে হবে যে, আমরা যদি ডেস্ট্রাক্টরকে ভার্চুয়াল না করি তবে সংকলক কেবল বেস ক্লাসের বিষয়বস্তুগুলিই বিকশিত করবে, n সমস্ত উত্পন্ন ক্লাসগুলি পরিবর্তিত থাকবে, ব্যাকিউস সংকলক অন্য কোনও ডেস্ট্রাক্টরকে কল করবে না বেস ক্লাস ছাড়া ক্লাস।


-১: কেন একজন ডেস্ট্রাক্টর ভার্চুয়াল হওয়া উচিত তা নিয়ে প্রশ্ন নেই।
ট্রাববাদৌর

তদুপরি, নির্দিষ্ট পরিস্থিতিতে সঠিক ধ্বংস সাধনের জন্য ধ্বংসকারীদের ভার্চুয়াল হতে হবে না। ভার্চুয়াল ডেস্ট্রাক্টরগুলি কেবল তখনই প্রয়োজন হয় যখন আপনি যখন deleteকোনও পয়েন্টারকে বেস ক্লাসে কল করে শেষ করেন যখন বাস্তবে এটি তার ডাইরিভেটিভকে নির্দেশ করে।
CygnusX1

আপনি 100% সঠিক। এটি অতীতে এবং সি ++ প্রোগ্রামের লিক এবং ক্র্যাশগুলির প্রথম একের উত্সগুলির মধ্যে অন্যতম এবং কেবলমাত্র নাল পয়েন্টার দিয়ে কাজ করার চেষ্টা করার এবং অ্যারের সীমা ছাড়িয়ে যাওয়ার তৃতীয়। ভার্চুয়াল চিহ্নিত না করা হলে সাব-ক্লাস ডেস্ট্রাক্টরকে পুরোপুরি বাইপাস করে জেনেরিক পয়েন্টারে একটি নন-ভার্চুয়াল বেস ক্লাস ডেস্ট্রাক্টরকে ডাকা হবে। সাবক্লাসের সাথে সম্পর্কিত যদি কোনও গতিশীলভাবে তৈরি বস্তু থাকে তবে মুছার জন্য কল করে বেস ডিস্ট্রাক্টর দ্বারা সেগুলি পুনরুদ্ধার করতে পারবে না। আপনি জরিমানা বরাবর আবার খনন করছেন! (কোথায় তা খুঁজে পাওয়া শক্ত))
ক্রিস রেড
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.