අතථ්‍ය විනාශ කරන්නන් භාවිතා කරන්නේ කවදාද?


1505

බොහෝ OOPන්‍යායන් පිළිබඳව මට ස්ථිර අවබෝධයක් ඇත, නමුත් මා බොහෝ ව්‍යාකූල කරන එක් දෙයක් වන්නේ අතථ්‍ය විනාශ කරන්නන් ය.

දාමයේ ඇති සෑම වස්තුවකටම කුමක් වුවත් විනාශකරුවා නිතරම කැඳවනු ඇතැයි මම සිතුවෙමි.

ඔබ ඒවා අථත්‍ය කිරීමට අදහස් කරන්නේ කවදාද සහ ඇයි?



147
පහතට වැටෙන සෑම විනාශකරුවෙකුම කුමක් වුවත් කැඳවනු ලැබේ. virtualඑය මැද වෙනුවට ඉහළින් ආරම්භ වන බවට වග බලා ගන්න.
මූයිං තාරා


OoMooingDuck එය තරමක් නොමඟ යවන ප්‍රකාශයකි.
යූරි පින්හෝලෝ

1
RanFranklinYu ඔබ ඇසීම හොඳය, මන්ද දැන් මට එම ප්‍රකාශය සමඟ කිසිදු ගැටළුවක් නොපෙනේ (අදහස් දැක්වීමේදී පිළිතුරු දීමට උත්සාහ කිරීම හැර).
යූරි පින්හෝලෝ

Answers:


1585

ව්‍යුත්පන්න පංතියක නිදසුනක් පාදක පන්තියට දර්ශකයක් හරහා මකා දැමිය හැකි විට අතථ්‍ය විනාශ කරන්නන් ප්‍රයෝජනවත් වේ:

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!

මූලික ගේ destructor නොවන බැවින් virtualසහ bවන්නේ Base*වෙත යොමු කර Derivedවස්තුව, delete bඇත නිර්වචනය නොකළ හැසිරීම :

[දී delete b], මකා දැමිය යුතු වස්තුවේ ස්ථිතික වර්ගය එහි ගතික වර්ගයට වඩා වෙනස් නම්, ස්ථිතික වර්ගය මකා දැමිය යුතු වස්තුවේ ගතික වර්ගයේ පාදක පන්තියක් විය යුතු අතර ස්ථිතික වර්ගයට අථත්‍ය විනාශ කරන්නෙකු හෝ හැසිරීම නිර්වචනය කර නැත .

බොහෝ ක්‍රියාත්මක කිරීම් වලදී, විනාශ කරන්නා වෙත කෙරෙන ඇමතුම ඕනෑම අථත්‍ය නොවන කේතයක් මෙන් විසඳනු ඇත, එයින් අදහස් වන්නේ මූලික පන්තියේ විනාශකරුවා කැඳවනු ඇති නමුත් ව්‍යුත්පන්න පංතියේ එකක් නොවන බැවින් සම්පත් කාන්දු වීමයි.

සාරාංශයක් ලෙස, සෑම විටම මූලික පංති විනාශ virtualකරන්නන් බහුඅවයවික ලෙස හැසිරවීමට අදහස් කරන විට ඒවා සාදන්න .

මූලික පංති දර්ශකයක් හරහා නිදසුනක් මකා දැමීම වලක්වා ගැනීමට ඔබට අවශ්‍ය නම්, ඔබට මූලික පංතියේ ඩිස්ට්‍රැක්ටරය ආරක්ෂිත සහ අථත්‍ය නොවන බවට පත් කළ හැකිය; එසේ කිරීමෙන්, සම්පාදකයා ඔබට deleteමූලික පන්තියේ දර්ශකයක් ඇමතීමට ඉඩ නොදේ .

මෙම ලිපියෙන් ඔබට අථත්යතාව සහ අථත්ය පාදක පන්ති විනාශ කරන්නා ගැන වැඩි විස්තර ඉගෙන ගත හැකිය .


177
මා විසින් මීට පෙර සාදන ලද කර්මාන්ත ශාලාවක් භාවිතා කරමින් විශාල කාන්දුවීම් සිදුවීමට හේතුව මෙයින් පැහැදිලි වේ. සියල්ල දැන් අර්ථවත් කරයි. ස්තූතියි
ලොඩ්ල්

9
හොඳයි, දත්ත සාමාජිකයන් නොමැති බැවින් මෙය නරක උදාහරණයකි. සියලුම ස්වයංක්‍රීය ගබඩා විචල්‍යයන් තිබේ නම් Baseසහ කුමක් කළ යුතුද? එනම් ඩිස්ට්‍රැක්ටරය තුළ ක්‍රියාත්මක කිරීමට “විශේෂ” හෝ අතිරේක අභිරුචි කේතයක් නොමැත. එසේනම් කිසියම් විනාශකරුවෙකු ලිවීම අත්හැර දැමීම හරිද? නැතහොත් ව්‍යුත්පන්න පන්තියට තවමත් මතක කාන්දුවක් තිබේද? Derived
bobobobo


29
ඔසු සූටර්ගේ ලිපියෙන්: "මාර්ගෝපදේශ # 4: මූලික පංති විනාශ කරන්නෙකු පොදු සහ අථත්ය හෝ ආරක්ෂිත හා අථත්ය විය යුතුය."
සන්ඩේ

4
ලිපියෙන් - 'ඔබ අතථ්‍ය විනාශ කරන්නෙකු නොමැතිව බහුමාමක ලෙස මකා දැමුවහොත්, ඔබ "නිර්වචනය නොකළ හැසිරීමේ" භයානක අවතාරය කැඳවයි, මධ්‍යස්ථව හොඳින් ආලෝකමත් වූ මංතීරුවක පවා මා පෞද්ගලිකව මුණගැසෙන්නේ නැති අවතාරයක්, ඔබට බොහෝම ස්තූතියි.' lol
බොන්ඩොලින්

221

අතථ්‍ය ඉදිකිරීම්කරුවෙකු කළ නොහැකි නමුත් අථත්‍ය විනාශ කරන්නෙකු විය හැකිය. අපි අත්හදා බලමු .......

#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

ව්‍යුත්පන්න වස්තුවක් තැනීම ඉදිකිරීම් රීතිය අනුගමනය කරන නමුත් අපි "ආ" දර්ශකය (පාදක දර්ශකය) මකා දැමූ විට අපට පෙනී ගියේ මූලික විනාශ කරන්නා පමණක් හැඳින්වෙන බවයි. නමුත් මෙය සිදු නොවිය යුතුය. සුදුසු දේ කිරීමට නම්, අප විසින් මූලික ඩිස්ට්‍රැක්ටර් අථත්‍ය බවට පත් කළ යුතුය. දැන් පහත දැක්වෙන දේ සිදුවන්නේ කුමක් දැයි බලමු:

#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
mecmeub, නමුත් අථත්ය ඉදිකිරීම්කරුවෙකුගෙන් ඔබට අවශ්ය දේ සාක්ෂාත් කර ගැනීම සඳහා මෝඩකමක් තිබේ. Parashift.com/c++-faq-lite/virtual-ctors.html
cape1232

UnTunvirRahmanTusher ඔබට කරුණාකර පැහැදිලි කළ හැකිද මූලික විනාශ කරන්නා ලෙස හඳුන්වන්නේ ??
rimalonfire

@rimiro එහි ස්වයංක්‍රීයව c ++. ඔබට stackoverflow.com/questions/677620/…
ටන්වීර් රහ්මාන් ටුෂර්

197

බහුමාධ්‍ය පාදක පන්තිවල ඩිස්ට්‍රැක්ටර්ස් අතථ්‍ය ලෙස ප්‍රකාශ කරන්න. මෙය ස්කොට් මේයර්ස්ගේ C ලදායී C ++ හි 7 වන අයිතමයයි . පංතියකට කිසියම් අතථ්‍ය ශ්‍රිතයක් තිබේ නම් එයට අථත්‍ය විනාශකාරකයක් තිබිය යුතු බවත්, මූලික පංති ලෙස නිර්මාණය කර නොමැති හෝ බහුමාමක ලෙස භාවිතා කිරීමට සැලසුම් කර නොමැති පන්ති අථත්‍ය විනාශ කරන්නන් ලෙස ප්‍රකාශ නොකළ යුතු බවත් මේයර්ස් සාරාංශ කරයි .


14
+ "පන්තියකට කිසියම් අථත්‍ය ශ්‍රිතයක් තිබේ නම්, එයට අථත්‍ය විනාශකාරකයක් තිබිය යුතු අතර, පංති මූලික පන්ති ලෙස නිර්මාණය කර නොමැති හෝ බහුඅවයවික ලෙස භාවිතා කිරීමට සැලසුම් කර නොමැති අතර අථත්‍ය විනාශ කරන්නන් ප්‍රකාශ නොකළ යුතුය.": එය අර්ථවත් වන අවස්ථා තිබේද? මෙම රීතිය කඩන්නද? එසේ නොවේ නම්, සම්පාදකයා විසින් මෙම තත්වය පරීක්ෂා කර දෝෂයක් නිකුත් කිරීම සෑහීමකට පත් නොවන්නේද?
ජෝර්ජියෝ

ජෝර්ජියෝ නීතියට ව්‍යතිරේකයක් ගැන මම නොදනිමි. නමුත් මම C ++ විශේෂ expert යෙකු ලෙස මා ඇගයීමට ලක් නොකරමි, එබැවින් ඔබට මෙය වෙනම ප්‍රශ්නයක් ලෙස පළ කිරීමට අවශ්‍ය විය හැකිය. සම්පාදක අනතුරු ඇඟවීමක් (හෝ ස්ථිතික විශ්ලේෂණ මෙවලමකින් අනතුරු ඇඟවීමක්) මට අර්ථවත් කරයි.
බිල් ද කටුස්සා

10
පංති සැලසුම් කළ හැක්කේ එක්තරා ආකාරයක දර්ශකයක් හරහා මකා නොදැමිය හැකි නමුත් තවමත් අතථ්‍ය කාර්යයන් ඇත - සාමාන්‍ය උදාහරණය ඇමතුම් ආපසු අතුරුමුහුණතකි. යමෙකු දායකත්වය සඳහා පමණක් වන බැවින් ඇමතුම් ආපසු අතුරුමුහුණතක් හරහා ඔහුගේ ක්‍රියාත්මක කිරීම මකා නොදමනු ඇත, නමුත් එයට අථත්‍ය කාර්යයන් ඇත.
dascandy

3
asc ඩැස්කාන්ඩි හරියටම - එය හෝ අපි බහුමාමක හැසිරීම් භාවිතා කරන නමුත් දර්ශකයන් හරහා ගබඩා කළමණාකරනය සිදු නොකරන වෙනත් බොහෝ අවස්ථා - උදා: ස්වයංක්‍රීය හෝ ස්ථිතික-කාල වස්තු නඩත්තු කිරීම, දර්ශක නිරීක්ෂණ මාර්ග ලෙස පමණක් භාවිතා කරයි. එවැනි ඕනෑම අවස්ථාවක අතථ්‍ය විනාශ කරන්නෙකු ක්‍රියාත්මක කිරීමේ අවශ්‍යතාවයක් / අරමුණක් නොමැත. අපි මෙහි සිටින පුද්ගලයින් උපුටා දක්වමින් සිටින බැවින්, මම ඉහළින් සිට සූටර්ට කැමතියි: "මාර්ගෝපදේශ # 4: මූලික පංති විනාශ කරන්නෙකු පොදු සහ අථත්‍ය හෝ ආරක්‍ෂිත හා අථත්‍ය විය යුතුය." පාදක දර්ශකයක් හරහා අහම්බෙන් මකා දැමීමට උත්සාහ කරන ඕනෑම කෙනෙකුට ඔවුන්ගේ මාර්ගවල දෝෂය පෙන්වන බව දෙවැන්න සහතික කරයි
underscore_d

1
IorGiorgio ඇත්ත වශයෙන්ම කෙනෙකුට විනාශ කරන්නෙකුට අථත්‍ය ඇමතුමක් භාවිතා කළ හැකි හා වළක්වා ගත හැකි උපක්‍රමයක් ඇත: ව්‍යුත්පන්න වස්තුවක් පාදකයට යොමු කිරීම වැනි සංයුක්තයක් හරහා බන්ධනය කරන්න const Base& = make_Derived();. මෙම අවස්ථාවේ දී, Derivedප්‍රත්‍යාවර්තයේ විනාශ කරන්නා එය අථත්‍ය නොවුනත් හැඳින්වේ, එබැවින් යමෙක් vtables / vpointers විසින් හඳුන්වා දුන් පොදු කාර්යය සුරකිනු ඇත. ඇත්ත වශයෙන්ම විෂය පථය තරමක් සීමිතය. ඇන්ඩ්‍රි ඇලෙක්සැන්ඩ්‍රෙස්කු සිය නූතන සී ++ නිර්මාණ පොතේ මේ ගැන සඳහන් කළේය .
vsoftco

46

අතථ්‍ය විනාශ කරන්නෙකු නොමැති විට මූලික පන්තියේ දර්ශකයක් මකා දැමීමෙන් නිර්වචනය නොකළ හැසිරීම් ඇති වන බව ද මතක තබා ගන්න . මම මෑතකදී ඉගෙන ගත් දෙයක්:

C ++ හි මකාදැමීම ඉක්මවා යා යුත්තේ කෙසේද?

මම අවුරුදු ගණනාවක් තිස්සේ C ++ භාවිතා කර ඇති අතර මම තවමත් එල්ලා මැරීමට සමත් වෙමි.


මම ඔබේ ප්‍රශ්නය දෙස බැලුවෙමි, ඔබ මූලික විනාශ කරන්නා අතථ්‍ය ලෙස ප්‍රකාශයට පත් කර ඇති බව දුටුවෙමි. එසේ නම්, “අථත්‍ය විනාශ කරන්නෙකු නොමැති විට මූලික පන්තියේ දර්ශකයක් මකා දැමීමෙන් නිර්වචනය නොකළ හැසිරීමක් ලැබෙනු ඇත” ඔබේ එම ප්‍රශ්නයට අදාළව වලංගු වේද? එම ප්‍රශ්නයේදී, ඔබ මකන්න යනුවෙන් හැඳින්වූ විට, ව්‍යුත්පන්න පන්තිය (එහි නව ක්‍රියාකරු විසින් නිර්මාණය කරන ලදි) පළමුව අනුකූල අනුවාදයක් සඳහා පරීක්ෂා කරනු ලැබේ. එය එහි එකක් සොයාගත් බැවින් එය හැඳින්වීය. එබැවින්, “විනාශ කරන්නෙකු නොමැති විට මූලික පන්තියේ දර්ශකයක් මකා දැමීමෙන් නිර්වචනය නොකළ හැසිරීමට හේතු වනු ඇත” යනුවෙන් පැවසීම වඩා හොඳ යැයි ඔබ සිතන්නේ නැද්ද?
ubuntugod

ඒකත් එකම දේ. පෙරනිමි ඉදිකිරීම්කරු අථත්‍ය නොවේ.
බිග්සැන්ඩ්විච්

42

ඔබේ පන්තිය බහුමාමක වන සෑම අවස්ථාවකම ඩිස්ට්‍රැක්ටර් අතථ්‍ය කරන්න.


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අවශ්‍යද? නැතහොත් එය භාෂාවෙන් ඇඟවෙන්නේද (අතහැර දැමීම ආරක්ෂිත කරයි)?
පොන්කඩූඩ්ල්

Al වල්ලකොලූ නැත, එය අවශ්‍ය විට පමණක් ප්‍රකාශ කරන්න. උදා: protectedකොටසට දැමීම, හෝ භාවිතා කිරීමෙන් එය අථත්‍ය බව සහතික කිරීම override.
ඇබික්ස්

9

අතුරුමුහුණත් සහ අතුරු මුහුණත් ක්‍රියාත්මක කිරීම ගැන සිතීමට මම කැමතියි. C ++ කථන අතුරුමුහුණත පිරිසිදු අතථ්‍ය පන්තියකි. විනාශ කරන්නා අතුරු මුහුණතේ කොටසක් වන අතර එය ක්‍රියාත්මක කිරීමට අපේක්ෂා කරයි. එබැවින් විනාශ කරන්නා පිරිසිදු අථත්‍ය විය යුතුය. ඉදිකිරීම්කරු ගැන කුමක් කිව හැකිද? ඉදිකිරීම්කරු ඇත්ත වශයෙන්ම අතුරුමුහුණතේ කොටසක් නොවේ.


2
එය එකම ප්‍රශ්නය පිළිබඳ වෙනස් ඉදිරිදර්ශනයකි. අපි පදනම් පංතිය එදිරිව ව්‍යුත්පන්න පංතිය වෙනුවට අතුරුමුහුණත් අනුව සිතන්නේ නම් එය ස්වාභාවික නිගමනයකි: එය අථත්‍ය බවට පත් කිරීමට වඩා අතුරු මුහුණතේ කොටසක් නම්. එය එසේ නොවේ නම්.
ඩ්‍රැගන් ඔස්ටොජික්

2
OO අතුරුමුහුණත සහ C ++ පිරිසිදු අථත්ය පන්තියේ සමානකම් සඳහන් කිරීම සඳහා +1 . විනාශ කරන්නා සම්බන්ධයෙන් ක්‍රියාත්මක කිරීමට අපේක්ෂා කෙරේ : එය බොහෝ විට අනවශ්‍යය. අමු ගතිකව වෙන් කරන ලද මතකය (උදා. විනාශ කරන්නෙකු (හෝ කිසියම් ශ්‍රිතයක්) virtualමූලික පන්තියක ප්‍රකාශයට පත් කරන්නේ නම්, එය ප්‍රකාශයට පත් නොකලත් එය ස්වයංක්‍රීයව virtualව්‍යුත්පන්න පන්තියක පවතින බව සලකන්න .
ඩේවිඩ්ආර්

විනාශ කරන්නා අනිවාර්යයෙන්ම අතුරු මුහුණතේ කොටසක් නොවන බවට තීරණාත්මක තොරතුරු මෙයින් මග හැරේ . බහුමාමක ක්‍රියාකාරිත්වයන් ඇති නමුත් ඇමතුම්කරුට කළමනාකරණය නොකරන / මකා දැමීමට ඉඩ නොදෙන පන්ති කෙනෙකුට පහසුවෙන් වැඩසටහන්ගත කළ හැකිය. එවිට අතථ්‍ය විනාශ කරන්නෙකුට අරමුණක් නැත. ඇත්ත වශයෙන්ම, මෙය සහතික කිරීම සඳහා, අථත්‍ය නොවන - බොහෝ විට පෙරනිමි - විනාශ කරන්නා පොදු නොවන විය යුතුය. මට අනුමාන කිරීමට සිදුවුවහොත්, මම කියන්නේ එවැනි පංති බොහෝ විට අභ්‍යන්තරව ව්‍යාපෘති සඳහා භාවිතා කරන බවයි, නමුත් එමඟින් මේ සියල්ලෙහි නිදසුනක් / සූක්ෂ්මතාවයක් ලෙස ඒවා එතරම් අදාළ නොවේ.
underscore_d

8

මූලික පංති දර්ශකය හරහා වස්තූන් මකා දමන අතරතුර විවිධ විනාශ කරන්නන් නිසි පිළිවෙලක් අනුගමනය කළ යුතු විට ඩිස්ට්‍රැක්ටර් සඳහා අතථ්‍ය මූල පදය අවශ්‍ය වේ. උදාහරණයක් වශයෙන්:

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

ඔබේ මූලික පංතියේ ඩිස්ට්‍රැක්ටර් අථත්‍ය නම් වස්තූන් අනුපිළිවෙලින් විනාශ වේ (පළමුව ව්‍යුත්පන්න වූ වස්තුව පසුව පදනම). ඔබේ මූලික පංති ඩිස්ට්‍රැක්ටරය අථත්‍ය නොවේ නම් මූලික පන්ති වස්තුව පමණක් මකා දැමෙනු ඇත (මක්නිසාද යත්, දර්ශකය මූලික පන්තියේ “Base * myObj” වේ). එබැවින් ව්‍යුත්පන්න වස්තුව සඳහා මතක කාන්දු වීමක් සිදුවනු ඇත.


7

සරලව කිවහොත්, වර්චුවල් ඩිස්ට්‍රැක්ටර් යනු ව්‍යුත්පන්න පංති වස්තුව වෙත යොමු වන මූලික පන්ති දර්ශකයක් මකා දැමූ විට නිසි පිළිවෙලට සම්පත් විනාශ කිරීමයි.

 #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කරන්නෙකු නොමැති අතර මූලික දර්ශකයක් ඇමතීමෙන් නිර්වචනය නොකළ හැසිරීමට තුඩු දෙයි.
ජේම්ස් ඇඩ්කිසන්

@ ජේම්ස් ඇඩ්කිසන් එය නිර්වචනය නොකළ හැසිරීමට හේතු වන්නේ ඇයි ??
rimalonfire

@rimiro එය ප්‍රමිතිය පවසන දෙයයි . මා සතුව පිටපතක් නොමැති නමුත් සබැඳිය ඔබව අදහස් දැක්වීමක් වෙත ගෙන යනු ඇත.
ජේම්ස් ඇඩ්කිසන්

@rimiro "එබැවින් මකාදැමීම මූලික පංති අතුරුමුහුණත හරහා බහුමාපක ලෙස සිදු කළ හැකි නම්, එය සැබවින්ම හැසිරිය යුතු අතර අථත්‍ය විය යුතුය. ඇත්ත වශයෙන්ම භාෂාවට එය අවශ්‍ය වේ - ඔබ අතථ්‍ය විනාශ කරන්නෙකු නොමැතිව බහුමාපක ලෙස මකා දැමුවහොත්, ඔබ භයානක අවතාරය කැඳවයි "නිර්වචනය නොකළ හැසිරීම," තරමක් හොඳින් ආලෝකමත් වූ මංතීරුවක පවා මා පෞද්ගලිකව මුණගැසෙන්නේ නැති අවතාරයක්, ඔබට බොහෝම ස්තූතියි. " ( gotw.ca/publications/mill18.htm ) - ඔසු සූටර්
ජේම්ස් ඇඩ්කිසන්

4

අතථ්‍ය පාදක පංති විනාශ කරන්නන් “හොඳම පුහුණුව” - මතක කාන්දු වීම වළක්වා ගැනීමට (හඳුනා ගැනීමට අපහසු) ඔබ සැමවිටම ඒවා භාවිතා කළ යුතුය. ඒවා භාවිතා කිරීමෙන්, ඔබේ පංතිවල උරුම දාමයේ සිටින සියලුම විනාශ කරන්නන් (නිසි පිළිවෙළට) කැඳවනු ලබන බවට ඔබට සහතික විය හැකිය. අතථ්‍ය ඩිස්ට්‍රැක්ටර් භාවිතා කරමින් මූලික පන්තියකින් උරුම කර ගැනීම, උරුම පන්තියේ විනාශකරුවා ස්වයංක්‍රීයව අථත්‍ය බවට පත් කරයි (එබැවින් ඔබට උරුම පන්ති ඩිස්ට්‍රැක්ටර් ප්‍රකාශයේ 'අථත්‍යය' නැවත ටයිප් කිරීමට අවශ්‍ය නැත).


4

ඔබ භාවිතා කරන්නේ නම් shared_ptr(shared_ptr, unique_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මූල පදය ඔබව බොහෝ වේදනාවෙන් බේරා ගත හැකිය.
මයිකල් එටීන්

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;
}

ඉහත නියැදියෙන් ඔබට පෙනෙනුයේ MakeUpper සහ MakeLower පන්තිය සඳහා වන විනාශ කරන්නා කැඳවනු නොලබන බවයි.

අතථ්‍ය ඩිස්ට්‍රැක්ටරය සමඟ ඊළඟ නියැදිය බලන්න

#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

මම හිතන්නේ මෙම ප්‍රශ්නයේ හරය අථත්‍ය ක්‍රම සහ බහුමාපකය ගැන මිස විනාශ කරන්නා ගැන නොවේ. මෙන්න පැහැදිලි උදාහරණයක්:

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.

අතථ්‍ය ඩිස්ට්‍රැක්ටර් භාවිතා කරන්නේ කවදාදැයි දැන් ඔබ තේරුම් ගත යුතුය.


නැත, මෙය නැවත කියවන්නේ අථත්‍ය ක්‍රියාකාරිත්වයේ මුලික කරුණු පමණක් වන අතර, විනාශ කරන්නා එක් විය යුත්තේ කවදාද / ඇයි යන සූක්ෂ්මතාවය මුළුමනින්ම නොසලකා හරිමින් - එය එතරම් බුද්ධිමත් නොවේ, එබැවින් OP ප්‍රශ්නය ඇසුවේ ඇයි. (එසේම, ඇයි මෙතන අනවශ්ය ගතික වෙන් යන්තම් ඇයි? B b{}; A& a{b}; a.foo();සඳහා පරීක්ෂා කිරීම. NULL- කළ යුතු nullptr- පෙර deleteවැරදි indendation සමග - - ආශා දනවන්නක් අවශ්ය නොවේ: delete nullptr;. එය කිසිදු-op පරිදි අර්ථ දක්වා ඇත දෙයක් නම්, ඔබට මෙම ඉල්ලා පෙර පරීක්ෂා කර ඇත යුතු ->foo(), newකෙසේ හෝ අසමත් වුවහොත් නිර්වචනය නොකළ හැසිරීම සිදුවිය හැකිය .)
underscore_d

2
එය අමතන්න කිරීම ආරක්ෂාකාරී වන අතර deleteමත NULL(එනම්, ඔබ අවශ්ය නැහැ පහිටුම් දක්වනය if (a != NULL)මුර).
ජේම්ස් ඇඩ්කිසන්

A සයිල්ස් ඩී ඔව්, මම දන්නවා. මගේ අදහස් දැක්වීමේදී
ජේම්ස් ඇඩ්කිසන්

1

අතථ්‍ය විනාශ කරන්නෙකු නොමැතිව මූලික පංතියක් (/ struct) හරහා මකාදැමීමේදී සිදුවිය හැකි “නිර්වචනය නොකළ” හැසිරීම හෝ අවම වශයෙන් “බිඳවැටීම” නිර්වචනය නොකළ හැසිරීම ගැන සාකච්ඡා කිරීම ප්‍රයෝජනවත් වනු ඇතැයි මම සිතුවෙමි. පහත කේතය සරල ව්‍යුහයන් කිහිපයක් ලැයිස්තුගත කරයි (පන්ති සඳහා ද එය සත්‍ය වේ).

#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;
*/
}

ඔබට අතථ්‍ය විනාශ කරන්නන් අවශ්‍යද නැද්ද යන්න මම යෝජනා නොකරමි, පොදුවේ ගත් කල ඒවා තිබීම හොඳ පුරුද්දකි. ඔබේ මූලික පන්තියට (/ struct) vtable නොමැති නම් සහ ඔබේ ව්‍යුත්පන්න පංතිය (/ struct) නොමැති නම් සහ ඔබ මූලික පන්තියක් (/ struct) හරහා වස්තුවක් මකා දැමුවහොත් ඔබ කඩාවැටීමකට ලක්වීමට හේතුව මම පෙන්වා දෙමි. දර්ශකය. මෙම අවස්ථාවේ දී, ඔබ ගොඩේ නිදහස් චර්යාවට යොමු කරන ලිපිනය අවලංගු වන අතර එමඟින් බිඳවැටීමට හේතුව.

ඔබ ඉහත කේතය ධාවනය කරන්නේ නම් ගැටළුව ඇති වූ විට ඔබට පැහැදිලිව පෙනෙනු ඇත. මූලික පංතියේ (/ struct) මෙම දර්ශකය ව්‍යුත්පන්න පංතියේ (/ struct) මෙම දර්ශකයට වඩා වෙනස් වූ විට ඔබ මෙම ගැටලුවට මුහුණ දීමට සිදුවේ. ඉහත නියැදියේ, struct a සහ b වල vtables නොමැත. c සහ d යන ව්‍යුහයන්ට vtables ඇත. මේ අනුව, a හෝ b වස්තුවක් ac හෝ d වස්තු නිදසුනකට vtable සඳහා ගණනය කෙරේ. මකාදැමීම සඳහා ඔබ මෙය a හෝ b පොයින්ටරයක් ​​පසු කළහොත් එය ගොඩවල් වල නිදහස් චර්යාවට ලිපිනය අවලංගු වීම නිසා බිඳ වැටෙනු ඇත.

මූලික පංති දර්ශකයන්ගෙන් vtables ඇති ව්‍යුත්පන්න අවස්ථා මකා දැමීමට ඔබ අදහස් කරන්නේ නම්, මූලික පන්තියට vtable එකක් ඇති බව සහතික කළ යුතුය. එය කළ හැකි එක් ක්‍රමයක් නම්, සම්පත් නිසි ලෙස පිරිසිදු කිරීම සඳහා ඔබට කෙසේ හෝ අවශ්‍ය විය හැකි අතථ්‍ය ඩිස්ට්‍රැක්ටරයක් ​​එක් කිරීමයි.


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

ප්‍රසිද්ධියේ, බහුමාමක හෝ නොවූ ඕනෑම පන්තියකට අතථ්‍ය විනාශ කරන්නෙකු සිටිය යුතුය. වෙනත් ආකාරයකින් කිවහොත්, එය මූලික පන්තියේ දර්ශකයක් මගින් පෙන්වා දිය හැකි නම්, එහි මූලික පන්තියට අතථ්‍ය විනාශ කරන්නෙකු සිටිය යුතුය.

අතථ්‍ය නම්, ව්‍යුත්පන්න පංති ඩිස්ට්‍රැක්ටර් කැඳවනු ලැබේ, එවිට මූලික පන්තියේ ඉදිකිරීම්කරු. අතථ්‍ය නොවේ නම්, මූලික පන්තියේ ඩිස්ට්‍රැක්ටර් පමණක් කැඳවනු ලැබේ.


මෙය අවශ්‍ය වන්නේ "එය මූලික පන්තියේ දර්ශකයක් මගින් පෙන්වා දිය හැකි නම්" සහ එය ප්‍රසිද්ධියේ මකා දැමිය හැකි යැයි මම කියමි . නමුත් පසුකාලීනව අවශ්‍ය වුවහොත් අතථ්‍ය dtors එකතු කිරීමේ පුරුද්දට වැටීම හානියක් නොවන බව මම සිතමි.
underscore_d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.