জিএনইউ জিসিসি (জি ++): কেন এটি একাধিক ডেটর তৈরি করে?


90

পরিবেশের বিকাশ: জিএনইউ জিসিসি (জি ++) ৪.১.২

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

আমি নিম্নলিখিত কোডটি ব্যবহার করে উপরে বর্ণিত যা চেষ্টা করেছি এবং পর্যবেক্ষণ করেছি।

"Test.h" এ

class BaseClass
{
public:
    ~BaseClass();
    void someMethod();
};

class DerivedClass : public BaseClass
{
public:
    virtual ~DerivedClass();
    virtual void someMethod();
};

"Test.cpp" এ

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

BaseClass::~BaseClass()
{
    std::cout << "BaseClass dtor invoked" << std::endl;
}

void BaseClass::someMethod()
{
    std::cout << "Base class method" << std::endl;
}

DerivedClass::~DerivedClass()
{
    std::cout << "DerivedClass dtor invoked" << std::endl;
}

void DerivedClass::someMethod()
{
    std::cout << "Derived class method" << std::endl;
}

int main()
{
    BaseClass* b_ptr = new BaseClass;
    b_ptr->someMethod();
    delete b_ptr;
}

আমি যখন উপরের কোডটি তৈরি করেছি (g ++ test.cpp -o পরীক্ষা) এবং তারপরে দেখি কী ধরণের চিহ্নগুলি নীচে তৈরি করা হয়েছে,

এনএম --demangle পরীক্ষা

আমি নিম্নলিখিত আউটপুট দেখতে পারে।

==== following is partial output ====
08048816 T DerivedClass::someMethod()
08048922 T DerivedClass::~DerivedClass()
080489aa T DerivedClass::~DerivedClass()
08048a32 T DerivedClass::~DerivedClass()
08048842 T BaseClass::someMethod()
0804886e T BaseClass::~BaseClass()
080488f6 T BaseClass::~BaseClass()

আমার প্রশ্নগুলি নিম্নরূপ।

1) কেন একাধিক ডিটার তৈরি করা হয়েছে (বেসক্লাস - 2, ডেরিভডক্লাস - 3)?

2) এই ডেক্টর মধ্যে পার্থক্য কি? কীভাবে এই একাধিক ডিটারগুলি বেছে বেছে ব্যবহার করা হবে?

আমার এখন অনুভূতি আছে যে সি ++ প্রকল্পের জন্য 100% ফাংশন কভারেজ অর্জন করার জন্য আমাদের এটি বুঝতে হবে যাতে আমি আমার ইউনিট পরীক্ষায় এই সমস্ত ডাক্তারকে অনুরোধ করতে পারি।

উপরের কেউ যদি আমাকে উত্তর দিতে পারে তবে আমি আন্তরিকভাবে প্রশংসা করব।


4
একটি সর্বনিম্ন, সম্পূর্ণ নমুনা প্রোগ্রাম অন্তর্ভুক্ত করার জন্য +1। ( sscce.org )
রোব ᵩ

4
আপনার বেস ক্লাসে ইচ্ছাকৃতভাবে কোনও অ-ভার্চুয়াল ডেস্ট্রাক্টর রয়েছে?
কেরেক এসবি

4
একটি ছোট পর্যবেক্ষণ; আপনি পাপ করেছেন এবং আপনার বেসক্লাস ডেস্ট্রাক্টরকে ভার্চুয়াল বানিয়েছেন না।
লাইক

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

4
@ লাইক: ভাল, আপনি যদি জানেন যে আপনি ঠিক আছে এমন একটি পয়েন্টার-টু-বেসের মধ্য দিয়ে প্রাপ্ত কোনও মুছে ফেলতে যাচ্ছেন না, আমি ঠিক নিশ্চিত করছিলাম ... মজাদারভাবে, আপনি যদি বেস সদস্যদের ভার্চুয়াল করে তোলেন, আপনি এমনকি আরও পেতে পারেন আরও ধ্বংসকারী
কেরেক এসবি

উত্তর:


74

প্রথমত, এই ফাংশনগুলির উদ্দেশ্যগুলি Itanium C ++ ABI তে বর্ণিত হয় ; "বেস অবজেক্ট ডেস্ট্রাক্টর", "সম্পূর্ণ অবজেক্ট ডেস্ট্রাক্টর", এবং "ডিস্ট্রাক্টর মোছা" এর অধীনে সংজ্ঞাগুলি দেখুন। ম্যাঙ্গেলড নামের ম্যাপিংটি 5.1.4 এ দেওয়া হয়েছে।

মূলত:

  • ডি 2 হ'ল "বেস অবজেক্ট ডেস্ট্রাক্টর"। এটি অবজেক্টটি নিজেই ধ্বংস করে, পাশাপাশি ডেটা সদস্য এবং নন-ভার্চুয়াল বেস ক্লাসগুলি।
  • ডি 1 হ'ল "সম্পূর্ণ বস্তু ধ্বংসকারী"। এটি অতিরিক্ত ভার্চুয়াল বেস ক্লাসগুলি ধ্বংস করে।
  • D0 হ'ল "মুছে ফেলা অবজেক্ট ডেস্ট্রাক্টর"। এটি সম্পূর্ণরূপে অবজেক্ট ডেস্ট্রাক্টর যা কিছু করে তা হ'ল প্লাস এটি operator deleteমেমরিটি মুক্ত করতে কল করে।

আপনার যদি ভার্চুয়াল বেস ক্লাস না থাকে তবে ডি 2 এবং ডি 1 অভিন্ন; জিসিসি পর্যাপ্ত অপ্টিমাইজেশনের স্তরে, উভয়ের জন্য একই কোডে প্রতীকের নাম রাখবে।


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

@ এসএমজি: ভার্চুয়াল উত্তরাধিকার সূত্রে, "ভার্চুয়াল" উত্তরাধিকার সূত্রে প্রাপ্ত শ্রেণিগুলি সর্বাধিক উদ্ভূত বস্তুর একমাত্র দায়বদ্ধতার অধীনে। এটি হ'ল যদি আপনার struct B: virtual Aতখন থাকে এবং তারপরে struct C: B, তখন আপনি যখন এমন কোনও Bআহ্বানকে ধ্বংস করেন B::D1যা ঘুরে দাঁড়ায় A::D2এবং যখন কোনও বিনাশ করার সময় Cআপনি C::D1কোনটি আহ্বান করেন B::D2এবং A::D2(নোট করুন কীভাবে B::D2একজন ধ্বংসকারীকে ডাকবেন না)। এই মহকুমায় যা সত্যই আশ্চর্যজনক তা হ'ল 3 ডিস্ট্রাক্টরের একটি সাধারণ লিনিয়ার শ্রেণিবিন্যাস সহ সমস্ত পরিস্থিতি পরিচালনা করতে সক্ষম হওয়া ।
ম্যাথিউ এম।

হুম, আমি বিষয়টি স্পষ্টভাবে বুঝতে পারি নি ... আমি ভেবেছিলাম প্রথম ক্ষেত্রে (বি অবজেক্টটি নষ্ট করে), এ :: ডি 2 এর পরিবর্তে এ :: ডি 1 শুরু করা হবে। এবং দ্বিতীয় ক্ষেত্রেও (সি অবজেক্টটি ধ্বংস করে), এ :: ডি 2 এর পরিবর্তে এ :: ডি 2 শুরু করা হবে। আমি কি ভূল?
Smg

এ :: ডি 1 অনুরোধ করা হয়নি কারণ এ এখানে উচ্চ-স্তরের শ্রেণি নেই; এ এর ভার্চুয়াল বেস ক্লাস ধ্বংস করার দায়িত্ব (যা উপস্থিত থাকতে পারে বা নাও থাকতে পারে) এ এর ​​অন্তর্গত নয়, বরং শীর্ষ স্তরের শ্রেণীর ডি 1 বা ডি 0 এর অন্তর্গত।
বিডনলান

37

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

না-ইন-চার্জ ctor এবং dtor ব্যবহার করা হয় যখন একটি বর্গ একটি অবজেক্ট যে অন্য বর্গ থেকে উত্তরাধিকারী ব্যবহার হ্যান্ডলিং virtualকীওয়ার্ডটি যখন বস্তুর সম্পূর্ণ অবজেক্ট নয় (তাই বর্তমান বস্তুর নির্মাণের বা destructing "অধীনে না" হয় ভার্চুয়াল বেস অবজেক্ট)। এই কর্টরটি ভার্চুয়াল বেস অবজেক্টের একটি পয়েন্টার গ্রহণ করে এবং এটি সংরক্ষণ করে।

ভারপ্রাপ্ত ctor এবং dtors সব অন্যান্য ক্ষেত্রে, অর্থাত যদি কোন ভার্চুয়াল উত্তরাধিকার জড়িত জন্য হয়; যদি বর্গ একটি ভার্চুয়াল বিনাশকারী আছে, ভারপ্রাপ্ত মোছার dtor পয়েন্টার, vtable স্লট মধ্যে যায় যখন একটি সুযোগ যে বস্তুর গতিশীল টাইপ জানেন (অর্থাত স্বয়ংক্রিয় বা স্ট্যাটিক স্টোরেজ সময়কাল সঙ্গে অবজেক্টের জন্য) ব্যবহার করবে ভারপ্রাপ্ত ডটর (কারণ এই স্মৃতিটি মুক্তি দেওয়া উচিত নয়)।

কোড উদাহরণ:

struct foo {
    foo(int);
    virtual ~foo(void);
    int bar;
};

struct baz : virtual foo {
    baz(void);
    virtual ~baz(void);
};

struct quux : baz {
    quux(void);
    virtual ~quux(void);
};

foo::foo(int i) { bar = i; }
foo::~foo(void) { return; }

baz::baz(void) : foo(1) { return; }
baz::~baz(void) { return; }

quux::quux(void) : foo(2), baz() { return; }
quux::~quux(void) { return; }

baz b1;
std::auto_ptr<foo> b2(new baz);
quux q1;
std::auto_ptr<foo> q2(new quux);

ফলাফল:

  • জন্য vtables প্রতিটি dtor এন্ট্রি foo, bazএবং quuxনিজ নিজ সময়ে বিন্দু ভারপ্রাপ্ত মোছার dtor।
  • b1এবং b2দ্বারা নির্মাণ করা হয় baz() ভারপ্রাপ্ত , যা কল foo(1) ভারপ্রাপ্ত
  • q1এবং ইনচার্জq2 দ্বারা নির্মিত quux() হয় , যা পড়েfoo(2) পূর্বে নির্মিত বস্তুর পয়েন্টার সহ ইনচার্জ এবং ইনচার্জ baz() নয়foo
  • q2~auto_ptr() ইনচার্জ দ্বারা ধ্বংস করা হয় , যা ভার্চুয়াল ডটরকে ~quux() ইনচার্জ মোছা বলে , যা ডাকে~baz() ইনচার্জ , ~foo() ইনচার্জ এবংoperator delete
  • q1ইনচার্জ দ্বারা ধ্বংস করা ~quux() হয় , যা কল ~baz() না ইনচার্জ এবং ~foo() ভারপ্রাপ্ত
  • b2~auto_ptr() ইনচার্জ দ্বারা ধ্বংস করা হয় , যা ভার্চুয়াল ডটর বলে calls~baz() ইনচার্জ মোছা বলে , যা ~foo() ইনচার্জ এবং ডাকেoperator delete
  • b1 দ্বারা ধ্বংস করা হয় ~baz() ভারপ্রাপ্ত , যা কল ~foo() ভারপ্রাপ্ত

যে কেউ থেকে প্রাপ্ত ব্যক্তি quuxএটির ইন-চার্জ কর্টর এবং ডটর ব্যবহার করবে এবং অবজেক্টটি তৈরি করার দায়িত্ব নেবে foo

নীতিগতভাবে, কোনও ভার্চুয়াল ভিত্তি নেই এমন শ্রেণীর জন্য কখনই ইন-চার্জ বৈকল্পিকের প্রয়োজন হয় না; সেক্ষেত্রে ইনচার্জ বৈকল্পিকটিকে কখনও কখনও একীভূত বলা হয় এবং / অথবা ইনচার্জ এবং ইনচার্জ নয় উভয়ের প্রতীকগুলি একটি একক বাস্তবায়নের সাথে যুক্ত হয় al


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

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

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

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