Не вызывается виртуальный деструктор производного класса
Есть решение, где динамическая библиотека (SharedLibrary) предоставляет интерфейс (абстрактный класс), а другой проект - его реализацию (производный класс). Концепция минимального примера:
Класс Website
выделяет 64 килобайта для буфера, куда помещает URL адрес. Далее, не покидая конструктор, симулируется вызов исключения. Ожидается, что деструктор Website
будет вызван, однако вызывается только деструктор базового класса InternetResource
:
Website::ctor
InternetResource::dtor
main::catch 'Oops!'
Следовательно, выделенная память не освобождается:
Решение
Исходный код
//
// Application/Main.cpp
//
#include <iostream>
#include "Website.hpp"
int main()
{
try
{
__debugbreak(); // for heap snapshot (1)
Website site("https://stackoverflow.com"); // throws
return 1; // unattainable
}
catch (const std::exception& e)
{
std::cout
<< "main::catch '"
<< e.what()
<< "'"
<< std::endl;
}
__debugbreak(); // for heap snapshot (2)
return 0;
}
//
// Application/Website.hpp
//
#pragma once
#include <SharedLibrary/InternetResource.hpp>
class Website : public InternetResource
{
public:
Website(const char* url);
virtual ~Website();
void* Buffer = nullptr;
};
//
// Application/Website.cpp
//
#include "Website.hpp"
#include <iostream>
#include <memory>
Website::Website(const char* url)
{
std::cout << "Website::ctor" << std::endl;
this->Buffer = malloc(1 << 16); // 64 kB
memcpy_s(this->Buffer, 1 << 16, url, strlen(url));
throw std::exception("Oops!");
}
Website::~Website()
{
std::cout << "Website::dtor" << std::endl;
free(this->Buffer);
this->Buffer = nullptr;
}
//
// SharedLibrary/Dll.hpp
//
#pragma once
#ifdef SHARED_LIBRARY_EXPORTS
# define SHARED_LIBRARY_API __declspec(dllexport)
#else
# define SHARED_LIBRARY_API __declspec(dllimport)
#endif
//
// SharedLibrary/InternetResource.hpp
//
#pragma once
#include "Dll.hpp"
class SHARED_LIBRARY_API InternetResource
{
public:
virtual ~InternetResource();
};
//
// SharedLibrary/InternetResource.cpp
//
#include "InternetResource.hpp"
#include <iostream>
InternetResource::~InternetResource()
{
std::cout << "InternetResource::dtor" << std::endl;
}
--
-- Premake5.lua
--
workspace "WhatTheDtor"
location("Build/")
configurations { "Debug" }
platforms { "x86" }
function cpp_commons()
language "C++"
cppdialect "C++17"
defines { "DEBUG" }
symbols "On"
runtime "Debug"
staticruntime "Off" -- /MDd
exceptionhandling ("Default") -- /EHsc
end
project "SharedLibrary"
kind "SharedLib"
cpp_commons()
files { "./SharedLibrary/**.cpp", "./SharedLibrary/**.hpp" }
defines { "SHARED_LIBRARY_EXPORTS" }
project "Application"
kind "ConsoleApp"
cpp_commons()
files { "./Application/**.cpp", "./Application/**.hpp" }
includedirs { "." }
dependson { "SharedLibrary" }
links { "SharedLibrary" }
Воспроизведение
- Соберите файлы по
Application
иSharedLibrary
. - Используйте premake5 для создания файлов решения.
premake5 vs2022
- Откройте './Build/WhatTheDtor.sln'.
- Соберите и начните отладку с профилированием кучи.
- Сделайте два снимка кучи на вшитых точках останова.
Вот и все. Здесь вы можете увидеть 64 килобайта неосвобожденной памяти.