私有析構函數和刪除析構函數
析構函數 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;
}