1. 程式人生 > 其它 >私有解構函式和刪除解構函式

私有解構函式和刪除解構函式

解構函式 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;
}