2017-10-12 77 views
0

以下代碼test-templated-destructor.cpp複製了我正在使用的庫的組織結構。我使用的是:在非模板類析構函數中刪除模板類指針?

$ cat /etc/issue 
Ubuntu 14.04.5 LTS \n \l 
$ g++ --version 
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4 
$ g++ -std=c++14 
g++: error: unrecognized command line option ‘-std=c++14’ 
g++: fatal error: no input files 
compilation terminated. 
$ g++ -std=c++11 
g++: fatal error: no input files 
compilation terminated. 

有:

  • 基類AA,並從它BBCC派生類;
  • 抽象類AAInstancer,和類從它AAInstancerTemplated衍生其是模板化
  • AAHandler類,它具有一個模板函數addTemplatedObject,其存儲AAInstancer*指針new AAInstancerTemplated<T>()目的,在上述類
  • map場所在main(),一個AAHandler對象實例化,並呼籲它.addTemplatedObject<BB>("BB");

如果我運行valgrind此,它報告:

==21000== 43 (16 direct, 27 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2 
==21000== at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==21000== by 0x40141B: void AAHandler::addTemplatedObject<BB>(std::string) (test-templated-destructor.cpp:64) 
==21000== by 0x40113E: main (test-templated-destructor.cpp:82) 

我認爲這個問題是我們在addTemplatedObject()使用new,因此,我們應該相應地刪除其最新的程序退出 - 但不這樣做,從而爲泄漏的原因。

因此,我認爲,要編寫通過instancers地圖循環迭代,並delete這幫指針在AAHandler的desctructor,但我不能:

  • 如果我寫:
~AAHandler() { 
    cout << " (running AAHandler destructor)" << endl; 
    map<string, AAInstancer*>::iterator it; 
    for (it = instancers.begin(); it != instancers.end(); it++) { 
     delete it->second; 
    } 
    } 

...然後我會在編譯:

$ g++ -g -Wall test-templated-destructor.cpp -o test-templated-destructor.exe 
test-templated-destructor.cpp: In destructor ‘AAHandler::~AAHandler()’: 
test-templated-destructor.cpp:60:18: warning: deleting object of abstract class type ‘AAInstancer’ which has non-virtual destructor will cause undefined behaviour [-Wdelete-non-virtual-dtor] 
     delete it->second; 
       ^

...這聽起來是對的 - AAInstancer沒有定義析構函數,所以編譯器可能會自動添加爲非虛擬的,導致此警告(儘管通過valgrind運行此操作將顯示不再檢測到泄漏)。

  • 如果我寫:
template <class T> 
    ~AAHandler() { 
     cout << " (running AAHandler destructor)" << endl; 
     map<string, AAInstancer*>::iterator it; 
     for (it = instancers.begin(); it != instancers.end(); it++) { 
     delete (AAInstancerTemplated<T>*)it->second; 
     } 
    } 

...在希望這個析構函數被調用,如果我們叫addTemplatedObject一些模板(它不會反正),編譯失敗:

$ g++ -g -Wall test-templated-destructor.cpp -o test-templated-destructor.exe && ./test-templated-destructor.exe 
test-templated-destructor.cpp:57:14: error: destructor ‘AAHandler::~AAHandler()’ declared as member template 
    ~AAHandler() { 
      ^

...這也是有意義的:AAHandler是一個非模板類,所以它的析構函數可能不應該被模板化。

那麼,是不是可以寫入AAHandler析構函數,這將delete所有new指針在其instancers,無論哪個模板,他們實例化 - 用最少的(或最佳,無),更改現有的代碼?

test-templated-destructor.cpp

// g++ -g -Wall test-templated-destructor.cpp -o test-templated-destructor.exe && ./test-templated-destructor.exe 
// valgrind --leak-check=yes ./test-templated-destructor.exe 

#include <iostream> 
#include <map> 
using namespace std; 

class AA { 
public: 
    string myname; 
    AA() { 
    myname = ""; 
    cout << " AA instantiated\n"; 
    } 
}; 


class BB : public AA { 
public: 
    string mystuff; 
    BB() { 
    mystuff = ""; 
    cout << " BB instantiated\n"; 
    } 
}; 

class CC : public AA { 
public: 
    string mythings; 
    CC() { 
    mythings = ""; 
    cout << " CC instantiated\n"; 
    } 
}; 

class AAInstancer 
{ 
public: 
    virtual AA* createInstance() = 0; 
    string tagName; 
}; 

template <class T> 
class AAInstancerTemplated: public AAInstancer 
{ 
public: 
    AA* createInstance() { 
     return new T(); 
    } 
}; 


class AAHandler 
{ 
public: 
    ~AAHandler() { } 
    AAHandler() { } 
    static map<string, AAInstancer*> instancers; 

    template <class T> 
    static void addTemplatedObject(string tagName) { 
     AAInstancer* instancer = new AAInstancerTemplated<T>(); 
     instancer->tagName = tagName; 
     instancers[tagName] = instancer; 
    } 

    AAHandler* get() { 
    if(singleton == NULL) 
     singleton = new AAHandler(); 
    return singleton; 
    } 
private: 
    static AAHandler* singleton; 
}; 
map<string, AAInstancer*> AAHandler::instancers; 



int main() 
{ 
    AAHandler aah; 
    aah.addTemplatedObject<BB>("BB"); 

    cout << "Address of aah: " << static_cast<void*>(&aah) << endl; 
    return 0; 
} 
+1

爲什麼不使用'的std ::的unique_ptr 的''而不是* AAInstancer'? – user0042

+1

如何'地圖<字符串的unique_ptr >'那麼你就不必做什麼? – CoryKramer

回答

1
AAInstancer

需要一個虛擬析構函數。 如果它不需要身體,你可以默認它。

virtual ~AAInstancer() = default; 
+0

謝謝@約翰 - 我嘗試這樣做: 「'警告:默認和刪除僅適用於-std = C++ 11或-std = GNU ++ 11'功能」;用'G ++ -g -std = C++ 11 -Wall ...'編譯通過,然而,不幸的是,同樣的泄漏是由'valgrind' – sdaau

+0

@sdaau沒有你compiiler支持這些建議的標誌檢測?你嘗試過嗎? – user0042

+0

@ user0042 - 是它,只是默認情況下不 - 這樣的編譯是OK,但 - 這就是爲什麼我說: 「用'G ++ -g -std = C++ 11 -Wall' ... passes_ _compiling」單獨改變仍然會泄漏內存。 – sdaau

1

使用std::unique_ptr<AAInstancer>:不是自己管理的內存

map<string, std::unique_ptr<AAInstancer>> 

爲成員。

+0

感謝@ user0042,'的std :: unique_ptr'也'-std = C++ 11'(否則'錯誤:「的unique_ptr」不是「std''成員即使'的#include ');但是當我'靜態地圖<串的std ::的unique_ptr >'替換instancers'的'的聲明和定義,而不是其他的OP改變,我得到「'在「靜態無效AAHandler實例:: addTemplatedObject .. -destructor.cpp:87:34:從這裏需要... -destructor.cpp:68:23:錯誤:不匹配'operator ='....'「現在我必須這樣做:... – sdaau

+0

@sdaau https://stackoverflow.com/questions/46714509/deleting-templated-class-pointers-in-a-non-templated-class-destructor/46714589#comment80375882_46714579 – user0042

+0

...'addTemplatedObject'我必須使用一些東西像'的std ::的unique_ptr instancer =標準::的unique_ptr (新AAInstancerTemplated ());',它與導致 「'錯誤:使用刪除功能的std ::的unique_ptr的」 ...'」 ...任何建議? – sdaau

0

OK,終於得到了一些工作這下正好c++11編譯罰款不漏; valgrind報道:

$ valgrind --leak-check=yes ./test-templated-destructor.exe==22888== Memcheck, a memory error detector 
==22888== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. 
==22888== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info 
==22888== Command: ./test-templated-destructor.exe 
==22888== 
Address of aah: 0xffefffb3f 
    (running AAHandler destructor) 
    ~AAInstancerTemplated <2BB> here; tref: 0x5a200b0 
    ~AAInstancer here 
    ~AAInstancerTemplated <2CC> here; tref: 0x5a201e0 
    ~AAInstancer here 
==22888== 
==22888== HEAP SUMMARY: 
==22888==  in use at exit: 0 bytes in 0 blocks 
==22888== total heap usage: 6 allocs, 6 frees, 198 bytes allocated 
==22888== 
==22888== All heap blocks were freed -- no leaks are possible 
==22888== 
==22888== For counts of detected and suppressed errors, rerun with: -v 
==22888== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 

是啊,無泄漏 - 我喜歡:)

的方法是有點經典的話:讓AAInstancerTemplated保留引用(這裏tref)到不管它是通過new實例,然後創建一個(AAInstancerTemplated)即delete是此參考文獻的析構函數。

請注意,即使在AAHandler,我們AAInstancer*存儲通用指針在instancers,而我們實例化模板對象(new AAInstancerTemplated<T>();) - 現在,有了這個組織,當我們delete it->second這是AAInstancer*型的,正確的模板析構函數被調用。

固定test-templated-destructor.cpp

// g++ -g -std=c++11 test-templated-destructor.cpp -o test-templated-destructor.exe && ./test-templated-destructor.exe 
// valgrind --leak-check=yes ./test-templated-destructor.exe 

#include <iostream> 
#include <map> 
#include <typeinfo> 
#include <functional> // note: uses std::function, which is c++11 feature 

using namespace std; 

class AA { 
public: 
    string myname; 
    AA() { 
    myname = ""; 
    cout << " AA instantiated\n"; 
    } 
}; 


class BB : public AA { 
public: 
    string mystuff; 
    BB() { 
    mystuff = ""; 
    cout << " BB instantiated\n"; 
    } 
}; 

class CC : public AA { 
public: 
    string mythings; 
    CC() { 
    mythings = ""; 
    cout << " CC instantiated\n"; 
    } 
}; 

class AAInstancer 
{ 
public: 
    virtual ~AAInstancer() { 
    cout << " ~AAInstancer here" << endl; 
    } 
    virtual AA* createInstance() = 0; 
    string tagName; 
}; 

template <class T> 
class AAInstancerTemplated: public AAInstancer 
{ 
public: 
    T* tref; 
    AA* createInstance() { 
    if (tref) delete tref; 
    tref = new T(); 
    return tref; 
    } 
    ~AAInstancerTemplated() { 
    cout << " ~AAInstancerTemplated <" << typeid(T).name() << "> here; tref: " << static_cast<void*>(&tref) << endl; 
    if (tref) delete tref; 
    } 
}; 


class AAHandler 
{ 
public: 
    ~AAHandler() { 
    cout << " (running AAHandler destructor)" << endl; 
    typedef typename map<string, AAInstancer*>::iterator instIterator; 
    for (instIterator it = instancers.begin(); it != instancers.end(); it++) { 
     delete it->second; 
    } 
    } 
    AAHandler() { } 
    static map<string, AAInstancer*> instancers; 

    template <class T> 
    static void addTemplatedObject(string tagName) { 
    AAInstancer* instancer = new AAInstancerTemplated<T>(); 
    instancer->tagName = tagName; 
    instancers[tagName] = instancer; 
    } 

    AAHandler* get() { 
    if(singleton == NULL) 
     singleton = new AAHandler(); 
    return singleton; 
    } 
private: 
    static AAHandler* singleton; 
}; 
map<string, AAInstancer*> AAHandler::instancers; 

int main() 
{ 
    AAHandler aah; 
    aah.addTemplatedObject<BB>("BB"); 
    aah.addTemplatedObject<CC>("CC"); 

    cout << "Address of aah: " << static_cast<void*>(&aah) << endl; 
    return 0; 
} 
+0

另請注意,這個例子適用於這個簡單的例子,但可能無法在更復雜的例子中使用,在這種情況下,如果(tref)刪除tref;'createInstance'! – sdaau