ভার্চুয়াল ডেস্ট্রাক্টর কখন ব্যবহার করবেন?


1485

আমার বেশিরভাগ ওও তত্ত্বের একটি দৃ understanding় ধারণা আছে তবে একটি জিনিস যা আমাকে অনেকটা বিভ্রান্ত করে তা হ'ল ভার্চুয়াল ডেস্ট্রাক্টর।

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

আপনি কখন তাদের ভার্চুয়াল করতে চান এবং কেন?



146
প্রত্যেকটি ডেস্ট্রাক্টর ডেকে আনা হয় যাই হোক না কেন। virtualএটি মধ্যের পরিবর্তে শীর্ষে শুরু হয় তা নিশ্চিত করে।
মাকিং হাঁস


@ মুভিংডাক যা কিছুটা বিভ্রান্তিকর মন্তব্য।
ইউরি পিনহলো

1
@ ফ্র্যাঙ্কলিনইয়ু আপনি যে জিজ্ঞাসা করেছেন তা ভাল কারণ এখন আমি মন্তব্যটির সাথে কোনও মন্তব্য দেখতে পাচ্ছি না (মন্তব্যে উত্তর দেওয়ার চেষ্টা করা বাদে)।
ইউরি পিনহলো

উত্তর:


1571

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

class Base 
{
    // some virtual methods
};

class Derived : public Base
{
    ~Derived()
    {
        // Do some important cleanup
    }
};

এখানে, আপনি বিজ্ঞপ্তি পাবেন যে আমি বেস এর বিনাশকারী হতে ডিক্লেয়ার করা হয়নি virtual। এখন, নীচের স্নিপেটটি একবার দেখে নেওয়া যাক:

Base *b = new Derived();
// use b
delete b; // Here's the problem!

যেহেতু বেসের ডেস্ট্রাক্টর নয় virtualএবং bএটি Base*কোনও Derivedবস্তুর প্রতি নির্দেশক , delete bতাই অপরিবর্তিত আচরণ :

[ইন delete b], যদি মুছে ফেলা বস্তুর স্থিতিশীল প্রকারটি তার গতিশীল প্রকারের থেকে পৃথক হয়, স্থির প্রকারটি মুছে ফেলার জন্য বস্তুর গতিশীল ধরণের একটি বেস শ্রেণি হবে এবং স্থির ধরণের ভার্চুয়াল ডেস্ট্রাক্টর বা আচরণ অনির্দিষ্ট হয়

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

সংক্ষিপ্তসার হিসাবে, সর্বদা বেস ক্লাসগুলির ডেস্ট্রাক্টরগুলি তৈরি করুন virtualযখন তাদের বোঝানো হচ্ছে বহুবর্ষে ম্যানিপুলেট করা।

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

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


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

8
কোনও ডেটা সদস্য নেই বলে এটি একটি খারাপ উদাহরণ। কী Baseএবং Derivedযদি সমস্ত স্বয়ংক্রিয় স্টোরেজ ভেরিয়েবল থাকে? অর্থাত্ ডেস্ট্রাক্টরে মৃত্যুর জন্য কোনও "বিশেষ" বা অতিরিক্ত কাস্টম কোড নেই। তাহলে কি ঠিক আছে কোনও লেখককে লেখার কাজ ছেড়ে দেওয়া? বা উদ্ভূত শ্রেণীর এখনও স্মৃতি ফাঁস হবে?
বোবোবোবো


28
ভেষজ সুটারের নিবন্ধ থেকে: "গাইডলাইন # 4: একটি বেস শ্রেণীর ডেস্ট্রাক্টর জনসাধারণ এবং ভার্চুয়াল, বা সুরক্ষিত এবং ভার্চুয়াল হওয়া উচিত" "
সুন্দে

3
এছাড়াও নিবন্ধটি থেকে - 'আপনি যদি ভার্চুয়াল ডেস্ট্রাক্টর ছাড়াই বহুত্বপূর্ণভাবে মুছে ফেলেন, তবে আপনি "অপরিজ্ঞাত আচরণ" এর ভয়ঙ্কর বর্ণনাকে ডেকে পাঠান, আমি ব্যক্তিগতভাবে এমনকি একটি মাঝারিভাবে ভাল-আলোকিত গলিতেও দেখা করতে পারি না, আপনাকে অনেক ধন্যবাদ।' lol
বন্ডলিন

219

ভার্চুয়াল কনস্ট্রাক্টর সম্ভব না তবে ভার্চুয়াল ডেস্ট্রাক্টর সম্ভব। আসুন পরীক্ষা করি .......

#include <iostream>

using namespace std;

class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

উপরের কোডটি আউটপুট দেয়:

Base Constructor Called
Derived constructor called
Base Destructor called

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

#include <iostream>

using namespace std;

class Base
{ 
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    virtual ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

নিম্নলিখিত হিসাবে আউটপুট পরিবর্তন হয়েছে:

Base Constructor Called
Derived Constructor called
Derived destructor called
Base destructor called

সুতরাং বেস পয়েন্টার ধ্বংস (যা উত্পন্ন বস্তুর উপর বরাদ্দ নেয়!) ধ্বংসের নিয়ম অনুসরণ করে, অর্থাৎ প্রথমে ডেরাইভড, তারপরে বেস। অন্যদিকে, ভার্চুয়াল কনস্ট্রাক্টরের মতো কিছুই নেই।


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

4
@ মুরকানটিলিজম, "ভার্চুয়াল কনস্ট্রাক্টরগুলি করা যায় না" সত্যই সত্য। কোনও কনস্ট্রাক্টরকে ভার্চুয়াল চিহ্নিত করা যায় না।
cmeub

1
@ কেমিউব, তবে ভার্চুয়াল নির্মাতার কাছ থেকে আপনি কী চান তা অর্জন করার জন্য একটি প্রতিমা রয়েছে। দেখুন parashift.com/c++-faq-lite/virtual-ctors.html
cape1232

@ তুনভীররহমানতুশের আপনি কি দয়া করে ব্যাখ্যা করতে পারবেন কেন বেস ধ্বংসকারী বলা হয় ??
রিমালনফায়ার

। @rimiro C দ্বারা ++, তার স্বয়ংক্রিয় আপনি যদি এই লিঙ্কটি অনুসরণ করতে পারেন stackoverflow.com/questions/677620/...
Tunvir রহমান তুষার

195

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


14
+ "যদি শ্রেণীর কোনও ভার্চুয়াল ফাংশন থাকে তবে এর একটি ভার্চুয়াল ডেস্ট্রাক্টর থাকতে হবে এবং যে ক্লাসগুলি বেস ক্লাস হিসাবে ডিজাইন করা হয়নি বা বহুবৈজ্ঞানিকভাবে ব্যবহারের জন্য ডিজাইন করা হয়নি সেগুলি ভার্চুয়াল ডেস্ট্রাক্টর হিসাবে ঘোষণা করা উচিত নয়।": এমন কোনও ক্ষেত্রে রয়েছে যা এটি বোঝায়? এই নিয়ম ভাঙবে? যদি তা না হয়, তবে কি এটি সন্তুষ্ট নয় কি সংস্থাপকটি এই শর্তটি পরীক্ষা করে ত্রুটি জারি করে বোঝাবেন?
জর্জিও

@ জর্জিও আমি এই নিয়মের কোনও ব্যতিক্রম জানি না। তবে আমি নিজেকে সি ++ বিশেষজ্ঞ হিসাবে রেট করব না, তাই আপনি এটি আলাদা প্রশ্ন হিসাবে পোস্ট করতে চাইতে পারেন want একটি সংকলক সতর্কতা (বা একটি স্ট্যাটিক বিশ্লেষণ সরঞ্জামের একটি সতর্কতা) আমার কাছে তাৎপর্যপূর্ণ।
বিল

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

3
@ ড্যাসাক্যান্ডি হুবহু - সেই বা অন্য অনেকগুলি পরিস্থিতিতে যেখানে আমরা বহুবর্ষাত্মক আচরণ ব্যবহার করি তবে পয়েন্টারগুলির মাধ্যমে স্টোরেজ ম্যানেজমেন্ট সম্পাদন করে না - যেমন স্বয়ংক্রিয় বা স্থির-স্থিতিকালীন অবজেক্টগুলি বজায় রাখা, পয়েন্টার কেবল পর্যবেক্ষণের রুট হিসাবে ব্যবহৃত হয়। এই জাতীয় কোনও ক্ষেত্রে ভার্চুয়াল ডেস্ট্রাক্টর বাস্তবায়নের কোনও প্রয়োজন / উদ্দেশ্য নেই। যেহেতু আমরা এখানে কেবল লোককে উদ্ধৃত করছি, আমি উপর থেকে সুটারকে পছন্দ করি: "গাইডলাইন # 4: একটি বেস শ্রেণীর বিন্যাসকারী পাবলিক এবং ভার্চুয়াল, বা সুরক্ষিত এবং অ ভার্চুয়াল হওয়া উচিত" " পরেরটি নিশ্চিত করে যে কোনও ব্যক্তি বেস পয়েন্টারের মাধ্যমে দুর্ঘটনাক্রমে মুছে ফেলার চেষ্টা করছে তাদের উপায়গুলির ত্রুটি দেখানো হয়েছে
আন্ডারস্কোর_

1
বেঁধে মাধ্যমে একটি const একটি বেস করার জন্য একটি উদ্ভূত বস্তুর রেফারেন্স, মত: @Giorgio আসলে একটি কৌতুক ব্যবহার এবং একটি বিনাশকারী করার জন্য একটি ভার্চুয়াল কল এড়াতে পারেন নেই const Base& = make_Derived();। এই ক্ষেত্রে, Derivedপ্রলিউটির ডেস্ট্রাক্টর বলা হবে, এটি ভার্চুয়াল না হলেও, কেউ ভিটিবেল / ভিপয়েন্টার দ্বারা প্রবর্তিত ওভারহেড সংরক্ষণ করে। অবশ্যই সুযোগটি বেশ সীমাবদ্ধ। আন্ড্রেই আলেকজান্দ্রেস্কু তাঁর আধুনিক আধুনিক সি ++ ডিজাইন বইয়ে এটি উল্লেখ করেছিলেন ।
vsoftco

46

এছাড়াও সচেতন থাকুন যে ভার্চুয়াল ডেস্ট্রাক্টর নেই যখন একটি বেস শ্রেণীর পয়েন্টার মুছে ফেলার ফলে অপরিবর্তিত আচরণের ফলাফল ঘটবে । আমি সম্প্রতি কিছু শিখেছি:

সি ++ এ কীভাবে মুছে ফেলা ওভাররাইড করা উচিত?

আমি বছরের পর বছর ধরে সি ++ ব্যবহার করছি এবং আমি এখনও নিজেকে স্তব্ধ করতে পারি।


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

বেশ একই জিনিস। ডিফল্ট কনস্ট্রাক্টর ভার্চুয়াল নয়।
বিগস্যান্ডউইচ

40

যখনই আপনার ক্লাসটি বহুকর্মী তখন ডেস্ট্রাক্টরকে ভার্চুয়াল করুন।


13

বেস ক্লাসে পয়েন্টারের মাধ্যমে ডেস্ট্রাক্টরকে কল করা

struct Base {
  virtual void f() {}
  virtual ~Base() {}
};

struct Derived : Base {
  void f() override {}
  ~Derived() override {}
};

Base* base = new Derived;
base->f(); // calls Derived::f
base->~Base(); // calls Derived::~Derived

ভার্চুয়াল ডেস্ট্রাক্টর কল অন্য কোনও ভার্চুয়াল ফাংশন কল থেকে আলাদা নয়।

কারণ base->f(), কলটি পাঠানো হবে Derived::f(), এবং এটির জন্য এটি একই base->~Base()- এর ওভাররাইডিং ফাংশন - এরDerived::~Derived() ডাকা হবে।

যখন ডেস্ট্রাক্টরকে অপ্রত্যক্ষভাবে ডাকা হয় তখন একই ঘটনা ঘটে delete base;deleteবিবৃতি ডাকব base->~Base()যা প্রেষিত করা হবে না Derived::~Derived()

অ-ভার্চুয়াল ডেস্ট্রাক্টর সহ বিমূর্ত শ্রেণি

আপনি যদি তার বেস শ্রেণীর পয়েন্টারের মাধ্যমে অবজেক্টটি মুছতে না যান - তবে ভার্চুয়াল ডেস্ট্রাক্টর লাগবে না। কেবল এটি তৈরি করুন protectedযাতে এটি দুর্ঘটনাক্রমে বলা হবে না:

// library.hpp

struct Base {
  virtual void f() = 0;

protected:
  ~Base() = default;
};

void CallsF(Base& base);
// CallsF is not going to own "base" (i.e. call "delete &base;").
// It will only call Base::f() so it doesn't need to access Base::~Base.

//-------------------
// application.cpp

struct Derived : Base {
  void f() override { ... }
};

int main() {
  Derived derived;
  CallsF(derived);
  // No need for virtual destructor here as well.
}

~Derived()সমস্ত উত্সযুক্ত শ্রেণিতে স্পষ্টভাবে ঘোষণা করা কি এটি ন্যায়সঙ্গতভাবে হওয়া দরকার ~Derived() = default? বা ভাষা দ্বারা বোঝানো (এটি বাদ দেওয়া নিরাপদ করে)?
পোনকডুডল

@ ওয়াল্লাকোলু নো, এটি যখন প্রয়োজন তখনই তা ঘোষণা করুন। যেমন করা protectedঅধ্যায়, বা তা নিশ্চিত করার জন্য এটি ব্যবহার করে ভার্চুয়াল এর override
অ্যাবিক্স

9

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


2
এটি একই প্রশ্নের ভিন্ন দৃষ্টিভঙ্গি। যদি আমরা বেস ক্লাস বনাম উত্পন্ন ক্লাসের পরিবর্তে ইন্টারফেসের বিবেচনা করি তবে এটি প্রাকৃতিক উপসংহার: যদি এটি ভার্চুয়াল তৈরির চেয়ে ইন্টারফেসের একটি অংশ হয়। যদি তা না হয়।
ড্রাগন অস্টোজিক

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

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

8

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

Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ; 

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


7

সহজ কথা বলতে গেলে, ভার্চুয়াল ডেস্ট্রাক্টর হ'ল সংস্থানগুলি যথাযথ ক্রমে বিনষ্ট করতে হয়, যখন আপনি একটি বর্গ শ্রেণীর পয়েন্টারকে ড্রেইড শ্রেণীর অবজেক্টের দিকে নির্দেশ করে delete

 #include<iostream>
 using namespace std;
 class B{
    public:
       B(){
          cout<<"B()\n";
       }
       virtual ~B(){ 
          cout<<"~B()\n";
       }
 };
 class D: public B{
    public:
       D(){
          cout<<"D()\n";
       }
       ~D(){
          cout<<"~D()\n";
       }
 };
 int main(){
    B *b = new D();
    delete b;
    return 0;
 }

OUTPUT:
B()
D()
~D()
~B()

==============
If you don't give ~B()  as virtual. then output would be 
B()
D()
~B()
where destruction of ~D() is not done which leads to leak


বেস ভার্চুয়াল ডেস্ট্রাক্টর না থাকলে এবং deleteবেস পয়েন্টারে কল করা অপরিজ্ঞাত আচরণের দিকে নিয়ে যায়।
জেমস অ্যাডকিসন

@ জামেস অ্যাডকিসন কেন এটি অপরিজ্ঞাত আচরণের দিকে পরিচালিত করে ??
রিমালনফায়ার

@rimiro স্ট্যান্ডার্ড যা বলে তা এটি । আমার একটি অনুলিপি নেই তবে লিঙ্কটি আপনাকে এমন একটি মন্তব্যে নিয়ে যায় যেখানে স্ট্যান্ডার্ডের মধ্যে কেউ অবস্থান উল্লেখ করে।
জেমস অ্যাডকিসন

@rimiro "যদি মুছে ফেলা হয়, সুতরাং, বেস শ্রেণীর ইন্টারফেসের মাধ্যমে যদি বহুত্বপূর্ণভাবে সম্পাদন করা যায়, তবে এটি অবশ্যই কার্যত আচরণ করবে এবং ভার্চুয়াল হতে হবে Indeed প্রকৃতপক্ষে, ভাষাটির এটির প্রয়োজন - যদি আপনি ভার্চুয়াল ডেস্ট্রাক্টর ছাড়াই বহুত্বপূর্ণভাবে মুছে ফেলেন, তবে আপনি তার ভয়ঙ্কর বর্ণনাকে ডেকে আনুন "অপরিবর্তিত আচরণ," আমি ব্যক্তিগতভাবে এমনকি একটি পরিচ্ছন্নভাবে আলোকিত গলিতেও সাক্ষাত করতে পারি না এমন একটি স্পেক্টর আপনাকে অনেক ধন্যবাদ। " ( getw.ca/publications/mill18.htm ) - হার্ব সটার
জেমস অ্যাডকিসন

4

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


4

আপনি যদি ব্যবহার করেন shared_ptr(কেবল শেয়ার্ড_পিটার, অনন্য_পাত্র নয়) তবে আপনার বেস শ্রেণীর ডেস্ট্রাক্টর ভার্চুয়াল থাকতে হবে না:

#include <iostream>
#include <memory>

using namespace std;

class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){ // not virtual
        cout << "Base Destructor called\n";
    }
};

class Derived: public Base
{
public:
    Derived(){
        cout << "Derived constructor called\n";
    }
    ~Derived(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    shared_ptr<Base> b(new Derived());
}

আউটপুট:

Base Constructor Called
Derived constructor called
Derived destructor called
Base Destructor called

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

3

ভার্চুয়াল ডেস্ট্রাক্টর কী বা ভার্চুয়াল ডেস্ট্রাক্টর কীভাবে ব্যবহার করতে হয়

ক্লাস ডেস্ট্রাক্টর এমন একটি ফাংশন যা পূর্ববর্তী শ্রেণীর একই নামের সাথে ~ যা শ্রেণীর দ্বারা বরাদ্দকৃত স্মৃতিটিকে পুনর্বিবেচিত করে। কেন আমাদের ভার্চুয়াল ডেস্ট্রাক্টর দরকার

কিছু ভার্চুয়াল ফাংশন সহ নীচের নমুনাটি দেখুন

নমুনাটি কীভাবে আপনি কোনও চিঠিটি উপরের বা নিম্নে রূপান্তর করতে পারেন তাও বলে

#include "stdafx.h"
#include<iostream>
using namespace std;
// program to convert the lower to upper orlower
class convertch
{
public:
  //void convertch(){};
  virtual char* convertChar() = 0;
  ~convertch(){};
};

class MakeLower :public convertch
{
public:
  MakeLower(char *passLetter)
  {
    tolower = true;
    Letter = new char[30];
    strcpy(Letter, passLetter);
  }

  virtual ~MakeLower()
  {
    cout<< "called ~MakeLower()"<<"\n";
    delete[] Letter;
  }

  char* convertChar()
  {
    size_t len = strlen(Letter);
    for(int i= 0;i<len;i++)
      Letter[i] = Letter[i] + 32;
    return Letter;
  }

private:
  char *Letter;
  bool tolower;
};

class MakeUpper : public convertch
{
public:
  MakeUpper(char *passLetter)
  {
    Letter = new char[30];
    toupper = true;
    strcpy(Letter, passLetter);
  }

  char* convertChar()
  {   
    size_t len = strlen(Letter);
    for(int i= 0;i<len;i++)
      Letter[i] = Letter[i] - 32;
    return Letter;
  }

  virtual ~MakeUpper()
  {
    cout<< "called ~MakeUpper()"<<"\n";
    delete Letter;
  }

private:
  char *Letter;
  bool toupper;
};


int _tmain(int argc, _TCHAR* argv[])
{
  convertch *makeupper = new MakeUpper("hai"); 
  cout<< "Eneterd : hai = " <<makeupper->convertChar()<<" ";     
  delete makeupper;
  convertch *makelower = new MakeLower("HAI");;
  cout<<"Eneterd : HAI = " <<makelower->convertChar()<<" "; 
  delete makelower;
  return 0;
}

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

ভার্চুয়াল ডেস্ট্রাক্টর সহ পরবর্তী নমুনা দেখুন

#include "stdafx.h"
#include<iostream>

using namespace std;
// program to convert the lower to upper orlower
class convertch
{
public:
//void convertch(){};
virtual char* convertChar() = 0;
virtual ~convertch(){}; // defined the virtual destructor

};
class MakeLower :public convertch
{
public:
MakeLower(char *passLetter)
{
tolower = true;
Letter = new char[30];
strcpy(Letter, passLetter);
}
virtual ~MakeLower()
{
cout<< "called ~MakeLower()"<<"\n";
      delete[] Letter;
}
char* convertChar()
{
size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
{
Letter[i] = Letter[i] + 32;

}

return Letter;
}

private:
char *Letter;
bool tolower;

};
class MakeUpper : public convertch
{
public:
MakeUpper(char *passLetter)
{
Letter = new char[30];
toupper = true;
strcpy(Letter, passLetter);
}
char* convertChar()
{

size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
{
Letter[i] = Letter[i] - 32;
}
return Letter;
}
virtual ~MakeUpper()
{
      cout<< "called ~MakeUpper()"<<"\n";
delete Letter;
}
private:
char *Letter;
bool toupper;
};


int _tmain(int argc, _TCHAR* argv[])
{

convertch *makeupper = new MakeUpper("hai");

cout<< "Eneterd : hai = " <<makeupper->convertChar()<<" \n";

delete makeupper;
convertch *makelower = new MakeLower("HAI");;
cout<<"Eneterd : HAI = " <<makelower->convertChar()<<"\n ";


delete makelower;
return 0;
}

ভার্চুয়াল ডেস্ট্রাক্টর ক্লাসের স্পষ্টতভাবে সবচেয়ে উত্পন্ন রান টাইম ডেস্ট্রাক্টরকে কল করবে যাতে এটি কোনও সঠিক উপায়ে বিষয়টিকে সাফ করতে সক্ষম হয়।

অথবা লিঙ্কটি দেখুন

https://web.archive.org/web/20130822173509/http://www.programminggallery.com/article_details.php?article_id=138


2

যখন আপনাকে বেস ক্লাস থেকে উদ্ভূত শ্রেণীর ডেস্ট্রাক্টর কল করতে হবে। আপনার বেস ক্লাসে ভার্চুয়াল বেস ক্লাস ডেস্ট্রাক্টর ঘোষণা করা দরকার।


2

আমি মনে করি এই প্রশ্নের মূলটি ভার্চুয়াল পদ্ধতি এবং বহুবচন সম্পর্কে, বিশেষত ধ্বংসকারীকে নয় not এখানে একটি পরিষ্কার উদাহরণ:

class A
{
public:
    A() {}
    virtual void foo()
    {
        cout << "This is A." << endl;
    }
};

class B : public A
{
public:
    B() {}
    void foo()
    {
        cout << "This is B." << endl;
    }
};

int main(int argc, char* argv[])
{
    A *a = new B();
    a->foo();
    if(a != NULL)
    delete a;
    return 0;
}

প্রিন্ট আউট করবে:

This is B.

virtualএটি ছাড়া প্রিন্ট আউট হবে:

This is A.

এবং এখন আপনার ভার্চুয়াল ডেস্ট্রাক্টরগুলি কখন ব্যবহার করবেন তা বোঝা উচিত।


না, এটি কেবল ভার্চুয়াল ফাংশনগুলির সম্পূর্ণ মৌলিক বিষয়গুলি পুনরায় প্রেরণ করে, ধ্বংসকারীকে কখন / কেন এক হতে হবে তার উপেক্ষাটিকে পুরোপুরি উপেক্ষা করে - এটি ততটা স্বজ্ঞাত নয়, সুতরাং ওপি কেন প্রশ্ন জিজ্ঞাসা করেছিল। (এছাড়াও, কেন অপ্রয়োজনীয় গতিশীল বরাদ্দ এখানে মাত্র না B b{}; A& a{b}; a.foo();জন্য চেক করা হচ্ছে। NULL- যা হওয়া উচিত nullptr- সামনে deleteing - ভুল indendation সাথে - প্রয়োজন নেই: delete nullptr;। একটি নো-অপ হিসাবে সংজ্ঞায়িত করা হয় কিছু, আপনাকে কল করার আগে এই চেক করা উচিত ছিল তাহলে ->foo(), অন্যথায় অপরিজ্ঞাত আচরণটি যদি newকোনওভাবে ব্যর্থ হয় তবেই ঘটতে পারে ))
আন্ডারস্কোর_ডি

2
পয়েন্টারে কল deleteকরা নিরাপদ NULL(যেমন, আপনার if (a != NULL)প্রহরী প্রয়োজন নেই )।
জেমস অ্যাডকিসন


1

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

#include <iostream>
using namespace std;

struct a
{
    ~a() {}

    unsigned long long i;
};

struct b : a
{
    ~b() {}

    unsigned long long j;
};

struct c : b
{
    ~c() {}

    virtual void m3() {}

    unsigned long long k;
};

struct d : c
{
    ~d() {}

    virtual void m4() {}

    unsigned long long l;
};

int main()
{
    cout << "sizeof(a): " << sizeof(a) << endl;
    cout << "sizeof(b): " << sizeof(b) << endl;
    cout << "sizeof(c): " << sizeof(c) << endl;
    cout << "sizeof(d): " << sizeof(d) << endl;

    // No issue.

    a* a1 = new a();
    cout << "a1: " << a1 << endl;
    delete a1;

    // No issue.

    b* b1 = new b();
    cout << "b1: " << b1 << endl;
    cout << "(a*) b1: " << (a*) b1 << endl;
    delete b1;

    // No issue.

    c* c1 = new c();
    cout << "c1: " << c1 << endl;
    cout << "(b*) c1: " << (b*) c1 << endl;
    cout << "(a*) c1: " << (a*) c1 << endl;
    delete c1;

    // No issue.

    d* d1 = new d();
    cout << "d1: " << d1 << endl;
    cout << "(c*) d1: " << (c*) d1 << endl;
    cout << "(b*) d1: " << (b*) d1 << endl;
    cout << "(a*) d1: " << (a*) d1 << endl;
    delete d1;

    // Doesn't crash, but may not produce the results you want.

    c1 = (c*) new d();
    delete c1;

    // Crashes due to passing an invalid address to the method which
    // frees the memory.

    d1 = new d();
    b1 = (b*) d1;
    cout << "d1: " << d1 << endl;
    cout << "b1: " << b1 << endl;
    delete b1;  

/*

    // This is similar to what's happening above in the "crash" case.

    char* buf = new char[32];
    cout << "buf: " << (void*) buf << endl;
    buf += 8;
    cout << "buf after adding 8: " << (void*) buf << endl;
    delete buf;
*/
}

আপনার ভার্চুয়াল ডেস্ট্রাক্টর দরকার কিনা তা আমি প্রস্তাব দিচ্ছি না, যদিও আমি সাধারণভাবে মনে করি এটিগুলি রাখা ভাল অভ্যাস। আমি যদি কেবল আপনার বেস ক্লাস (/ স্ট্রাক্ট) এর একটি ভেটেবল না পেয়ে এবং আপনার উত্পন্ন শ্রেণি (/ স্ট্রাক্ট) না করে এবং আপনি কোনও বেস ক্লাস (/ স্ট্রাক্ট) এর মাধ্যমে কোনও বস্তু মুছতে পারেন তবে আপনি ক্র্যাশ হয়ে যাওয়ার কারণটি কেবল ইঙ্গিত করছি / পয়েন্টার। এই ক্ষেত্রে, আপনি স্তূপের মুক্ত রুটিনে প্রবেশের ঠিকানাটি অবৈধ এবং সুতরাং ক্রাশের কারণ the

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

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


0

সম্পর্কে একটি প্রাথমিক সংজ্ঞা virtual হ'ল এটি নির্ধারণ করে যে কোনও শ্রেণীর সদস্য ফাংশন তার উত্পন্ন ক্লাসগুলিতে অতি-চাপিত হতে পারে।

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

নির্দেশটি কার্যকর হওয়ার সাথে সাথেই বেস ক্লাসের ডেস্ট্রাক্টরকে ডেকে পাঠানো হয়, তবে প্রাপ্ত উত্সটির জন্য নয়।

একটি প্রকট উদাহরণ হ'ল কন্ট্রোল ফিল্ডে আপনাকে কার্যকরকারী, অ্যাকিউউটরদের পরিচালনা করতে হবে।

সুযোগের শেষে, যদি কোনও একটি পাওয়ার অ্যালিমেন্ট (অ্যাকিউউটর) এর ডেস্ট্রাক্টরকে ডাকা না হয় তবে মারাত্মক পরিণতি হবে।

#include <iostream>

class Mother{

public:

    Mother(){

          std::cout<<"Mother Ctor"<<std::endl;
    }

    virtual~Mother(){

        std::cout<<"Mother D-tor"<<std::endl;
    }


};

class Child: public Mother{

    public:

    Child(){

        std::cout<<"Child C-tor"<<std::endl;
    }

    ~Child(){

         std::cout<<"Child D-tor"<<std::endl;
    }
};

int main()
{

    Mother *c = new Child();
    delete c;

    return 0;
}

-1

যে সকল শ্রেণীর প্রকাশ্যে উত্তরাধিকারসূত্রে প্রাপ্ত, বহুকোষী বা না, তার ভার্চুয়াল ডেস্ট্রাক্টর থাকা উচিত। অন্য উপায়ে বলতে গেলে, যদি এটি একটি বেস শ্রেণীর পয়েন্টার দ্বারা নির্দেশিত হতে পারে তবে এর বেস শ্রেণিতে ভার্চুয়াল ডেস্ট্রাক্টর থাকা উচিত।

যদি ভার্চুয়াল হয়, উত্পন্ন শ্রেণি ধ্বংসকারীকে ডেকে আনা হয়, তবে বেস বর্গ নির্মাণকারী। ভার্চুয়াল না হলে, কেবল বেস শ্রেণীর ধ্বংসকারীকে কল করা হয়।


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