2014-01-23 60 views
8

C++編譯器是否會爲這個「類」生成類似於構造函數/析構函數/複製構造函數的默認函數?C++是否爲純虛擬類創建默認的「構造器/析構函數/複製構造函數/複製賦值運算符」?

class IMyInterface 
{ 
    virtual void MyInterfaceFunction() = 0; 
} 

我的意思是不可能實例化這個「類」,所以我認爲沒有默認函數生成。 否則,人們說你必須使用虛擬析構函數。 這意味着如果我不定義析構函數虛擬它將被默認創建,而不是虛擬的。

此外,我想知道是否合理的定義一個虛擬析構函數爲純虛擬接口,就像上面那個? (所以沒有指針或數據在這裏使用,所以沒有什麼必須被破壞)

謝謝。

+4

'= 0;'只允許跟隨虛擬功能。 – billz

+3

這不是C++。 –

+0

@billz和軌道亮度競賽:修正了它 – user1911091

回答

1

此外我想知道爲純虛擬接口定義一個虛擬析構函數是否合理,就像上面那樣? (所以沒有指針或數據在這裏使用,所以沒有什麼必須被破壞)

這不僅是合理的,它的建議。這是因爲在虛函數層次結構的情況下,(自動)調用專用類的析構函數也會調用它的基類的所有析構函數。如果他們沒有定義,你應該得到一個鏈接錯誤。

如果你至少在你的類中定義了一個虛擬函數,你還應該定義一個虛擬析構函數。

析構函數可以與=default雖然進行定義:

這裏的一個校正(編譯)的代碼示例:

class ImyInterface 
{ 
    virtual void myInterfaceFunction() = 0; 
    virtual ~ImyInterface() = 0; 
} 

ImyInterface::~ImyInterface() = default; 
+2

定義需要放在'.cpp'文件中,否則你應該添加'inline'關鍵字。另外,當另一個成員已經將該類標記爲抽象類時,將析構函數定義爲純虛函數又有什麼意義? – Potatoswatter

3

是。

沒有措辭要求該類是可實例化的,以便這些特殊的成員函數被隱式聲明。

這很有道理—只是因爲你不能實例化Base,並不意味着Derived類不想使用這些函數。

struct Base 
{ 
    virtual void foo() = 0; 
    int x; 
}; 

struct Derived : Base 
{ 
    Derived() {};   // needs access to Base's trivial implicit ctor 
    virtual void foo() {} 
}; 

參見:

  • § 12.1/5(構造函數)
  • § 12.8/9(移動)
  • § 12.8/20(複製)
+3

「不想使用這些函數」當然是一種非常柔和的方式 - 我沒有看到構造函數和析構函數甚至可以避免它。畢竟,在派生類構造函數的主體進入之前調用基類構造函數,反之亦然析構函數。 –

+0

@ChristopherCreutzig:的確如此。 –

1

此外,我想知道定義一個虛擬機是否合理一個純虛擬接口的析構函數,就像上面那個? (所以沒有指針或數據在這裏使用,所以沒有什麼必須被破壞)

派生類會在其析構函數中做什麼嗎?即使在別人接管發展的時候,你能否確定他們永遠不會?

擁有虛擬析構函數的重點不在於確保基類被正確地銷燬,反正會發生。問題的關鍵是,當你使用一個通用的接口派生類的析構函數被調用:

struct A { 
    virtual ~A() {} 
    virtual int f() = 0; 
}; 

class B : public A { 
    std::ifstream fh; 
public: 
    virtual ~B() {} 
    virtual int f() { return 42; } 
}; 

std::shared_ptr<A> a = new B; 

a超出範圍,爲什麼是ifstream關閉?因爲析構函數使用虛擬析構函數刪除對象

0

這解決了關於爲抽象基類聲明虛擬析構函數的第二個問題(例如,至少一個成員函數是純虛函數)。這是LLVM clang ++編譯器捕捉潛在問題的真實例子。這發生在Apple Developer爲Mac OS X Mavericks操作系統提供的命令行工具版本中。

假設您有一個派生類的集合,最終派生類具有抽象基類的父類來定義公共接口。然後有必要有一個存儲容器,像一個有意聲明存儲指向每個元素的抽象基類的指針的向量。稍後,遵循良好的工程實踐,需要「刪除」容器元素並將內存返回到堆中。最簡單的方法是按元素遍歷vector元素並調用每個元素的delete操作。

好吧,如果抽象基類沒有將析構函數聲明爲虛擬的,那麼clang ++編譯器會提供一個關於在抽象類上調用非虛析構函數的友好警告。請記住,實際上只有派生類是從操作符new的堆中分配的。從繼承關係派生的類指針類型的確是抽象的基類類型(例如is-a關係)。

如果抽象基類析構函數不是虛擬的,那麼將如何調用正確派生類的析構函數來釋放內存?至多編譯器知道更好(至少可能用C++ 11),並使其發生。如果啓用了-Wall編譯器選項,則至少會出現編譯警告。然而,更糟糕的是,派生類析構函數從來沒有到達過,內存永遠不會返回到堆中。因此,現在有一個內存泄漏,追查和修復可能非常具有挑戰性。它只需要在抽象基類析構函數聲明中添加一個「虛擬」。

示例代碼:

class abstractBase 
{ 
    public: 
     abstractBase() { }; 
     ~abstractBase() { }; 

     virtual int foo() = 0; 
}; 


class derived : abstractBase 
{ 
    public: 
     derived() { }; 
     ~derived() { }; 

     int foo() override { return 42; } 
}; 

// 
// Later on, within a file like main.cpp . . . 
// (header file includes are assumed to be satisfied) 
// 
vector<abstractBase*> v; 

for (auto i = 0; i < 1000; i++) 
{ 
    v.push_back(new derived()); 
} 



// 
// do other stuff, logic, what not 
// 


// 
// heap is running low, release memory from vector v above 
//  
for (auto i = v.begin(); i < v.end(); i++) 
{ 
    delete (*i); // problem is right here, how to find the derived class' destructor? 
} 

要解決此潛在內存泄漏,抽象基類有聲明它的析構作爲虛擬的。沒有其他要求。抽象基類現在變爲:

class abstractBase 
{ 
    public: 
     abstractBase() { }; 
     virtual ~abstractBase() { }; // insert virtual right here 

     virtual int foo() = 0; 
} 

請注意,抽象基類當前具有空構造函數和析構函數體。正如Lightness在上面回答的那樣,編譯器爲抽象基類創建了一個默認構造函數,析構函數和複製構造函數(如果工程師沒有定義的話)。強烈建議您查看C++創建者Bjarne Stroustrup的任何C++編程語言版本,以獲取有關抽象基類的更多詳細信息。

相關問題