私有析構函數和刪除析構函數

析構函數 destructor

私有析構函數 private destructor

析構函數是 private 時,那麼當對象銷毀時或對於動態分配的對象,當對指向它的指針應用 delete 運算符時,只有 對象銷毀 所處的上下文環境為成員函數體或友元函數體內部時,才能正常調用 private 析構函數,才能正常銷毀對象。其原因是,只有成員函數和友元函數才能訪問類的 private 成員。

所以,(1)當定義一個對象(非動態分配)時,只有 對象定義 所處的上下文環境能夠訪問 private 析構函數時,編譯器才會允許定義該類型的對象。其原因是,如果沒有這條規則,我們可能會創建出無法銷毀的對象。(2)動態分配的對象, new 表達式所處的環境並沒有什麼限制,但當對指向它的指針應用 delete 運算符時,只有 delete 表達式所處的上下文環境能夠訪問 private 析構函數時,才能正常銷毀對象。

其實,將析構函數聲明為 private 幾乎沒有什麼實用意義,因為通常我們的程式設計應該總是使析構函數能夠被通暢地調用,而不是設置「障礙」。

#include <iostream>
#include <string>
#include <memory>

class T
{
	friend void FriendDestroy(T* ptr);
public:
	static void StaticDestroy(T* ptr)
	{
		delete ptr;
	}

	// Destroy() is 'delete this', which looks like very dangerous, not recommended. The more recommended is 'static void StaticDestroy(T* ptr)'.
	void Destroy()
	{
		delete this;
	}
public:
	void InstanceInMemberFunction()
	{
		// ok: destructor 'T::~T()' can normally call within this context
		T t;
	}
public:
	T() = default;
	T(const T&) = default;
	T(T&&) = default;
	T& operator=(const T&) = default;
	T& operator=(T&&) = default;
private:
	~T() { std::cout << "destructor is called" << std::endl; }
private:
	std::string str_;
};

void FriendDestroy(T* ptr)
{
	delete ptr;
}

int main()
{
	// error: destructor 'T::~T()' is private within this context
	//T t1;

	// error: destructor 'T::~T()' is private within this context where '~unique_ptr()' delete pointer
	//auto t2 = std::make_unique<T>();

	// no delete pointer and the memory leak would occur
	auto t3 = new T();
	// error: destructor 'T::~T()' is private within this context
	//delete t3;

	auto t4 = new T();
	// Destroy() is 'delete this', which looks like very dangerous, not recommended. The more recommended is 'static void StaticDestroy(T* ptr)'.
	t4->Destroy();
	t4 = nullptr;

	auto t5 = new T();
	t5->InstanceInMemberFunction();
	T::StaticDestroy(t5);
	t5 = nullptr;

	auto t6 = new T();
	FriendDestroy(t6);
	t6 = nullptr;

	return 0;
}

刪除析構函數 deleted destructor

值得注意的是,我們不能刪除析構函數。如果析構函數被刪除,就無法銷毀此類型的對象了。對於一個刪除了析構函數的類型,編譯器將不允許定義該類型的變數或創建該類的臨時對象。而且,如果一個類有某個成員的類型刪除了析構函數,我們也不能定義該類的變數或臨時對象。因為如果一個成員的析構函數是刪除的,則該成員無法被銷毀。而如果一個成員無法被銷毀,則對象整體也就無法被銷毀了。

對於刪除了析構函數的類型,雖然我們不能定義這種類型的變數或成員,但可以動態分配這種類型的對象。但是,不能釋放這些對象。

其實,刪除析構函數幾乎沒有什麼實用意義,因為我們很少需要創建不能銷毀的對象(這時就只能通過動態分配來創建對象了,但不能釋放這些對象)。

#include <iostream>
#include <string>
#include <memory>

class T
{
public:
	~T() = delete;
public:
	T() = default;
	T(const T&) = default;
	T(T&&) = default;
	T& operator=(const T&) = default;
	T& operator=(T&&) = default;
private:
	std::string str_;
};

int main()
{
	// error: use of deleted destructor function 'T::~T()'
	//T t1;

	// error: use of deleted destructor function 'T::~T()'
	//auto t2 = std::make_unique<T>();

	// no delete pointer and the memory leak would occur
	auto t3 = new T();
	// error: use of deleted destructor function 'T::~T()'
	//delete t3;

	return 0;
}
Tags: