以下代码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
,以及从中派生的类BB
和CC
; 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)
我认为问题是我们在new
中使用了addTemplatedObject()
,因此我们应该在程序退出时相应地删除它 - 但是没有完成,因此泄漏的原因。
所以我想,编写一个循环遍历instancers
映射的迭代器,并delete
在AAHandler
的析构函数中使用 ~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
没有定义析构函数,因此编译器可能自动添加为非虚拟,导致此警告(尽管通过{{运行此操作) 1}}将显示不再检测到泄漏。)
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;
}
答案 0 :(得分:1)
AAInstancer
需要一个虚拟析构函数。
如果它不需要身体,你可以默认它。
virtual ~AAInstancer() = default;
答案 1 :(得分:1)
答案 2 :(得分:0)
好的,终于有了一些工作,只在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)
是的,没有泄漏 - 我喜欢:)
这种方法有点经典:make AAInstancerTemplated
将引用(此处为tref
)保存到通过new
实例化的任何内容,然后为其创建析构函数(for {{1} } {} AAInstancerTemplated
这个引用。
请注意,即使在delete
中,我们也会在AAHandler
中存储通用指针AAInstancer*
,而我们实例化模板化对象(instancers
) - 现在,使用此组织,当我们new AAInstancerTemplated<T>();
类型为delete it->second
时,会调用正确的模板化析构函数。
固定的AAInstancer*
:
test-templated-destructor.cpp