2014-09-26 209 views
6

如果只定義這個方法,它將會有一個編譯器錯誤。爲什麼必須在C++類定義中聲明一個方法?

void classA::testMethod() {  
} 

所以它必須首先聲明:

class classA {  
    void testMethod(); 
}; 

爲什麼這些聲明?

我知道它是不是需要申報常見的C方法的方法,但是他們可以定義爲:

void test() { 
} 
+0

你可以在c中聲明類之外的函數 – jonynz 2014-09-26 09:01:09

+0

@jonynz:這沒有意義,C沒有類或方法。 – MSalters 2014-09-26 09:06:04

+0

採取了一點,謝謝 – jonynz 2014-09-26 09:07:34

回答

7

有幾個次要的原因客觀和主觀(即允許指定可見性,作爲類的接口,可能還有其他幾個與編譯和鏈接階段和TU符號有關的其他原因的可見性,更不用提一類是基本封裝單元,與所有的暗示),但不容置疑之一是的標準規定它

N3797 - class.mfct/P2

甲成員函數可以在i中定義(8.4) ts類的定義,在 這種情況下它是內聯成員函數(7.1.2),或者它可能是在其類定義之外定義的 ,如果它已經聲明瞭但未在其類定義中定義的 。在類定義之外出現的成員函數 定義應在包含類定義的名稱空間範圍中出現 。除類別定義以外的成員 功能定義和 類別 類別的成員函數的顯式特殊化和成員函數模板(14.7)出現在類別定義之外的 以外,不應重新聲明成員函數。

強調我的。

+1

不用擔心,謝謝你解決這個問題! – 2014-09-26 12:15:00

6

它有助於封裝。如果你有一個A級

class A { 
public: 
    foo(); 
    bar(); 
} 

你可以肯定,只有方法foobar惹類的私有數據成員。 (或指針魔術或當然未定義的行爲)

+0

我不明白這是如何相關的。 – user2864740 2014-09-26 09:01:14

+0

@ user2864740可以定義獨立的'void classA :: testMethod(){...}'表示您想要擴展該類。但在C++中情況並非如此。 – Csq 2014-09-26 09:03:33

+0

封裝與這些有什麼關係? C++ *可能被定義爲執行一些愚蠢的魔術,例如將這些找到的定義提升爲類定義(這沒什麼意義,特別是鑑於定義通常與實現分離)。但這些都與封裝無關。 – user2864740 2014-09-26 09:06:22

1

why should declare these? I know for common c method, there is no need to declare, instead of it just define it:

有一個在C,一個結構的只是屬性沒有方法,至極可以是功能Pointeur,然後關聯到一個功能addresse。

而且必須聲明它的類定義爲你做它在C同樣的原因:

的compilateur將改變這種預申報成一個函數pointeur再聯想到上述梅索德INT的建設你的對象。

如果一個C的定義++類應該被轉換成C結構的代碼會是這樣:

struct Aclass { 
void (*Amethode))(int); 
} 
void Amethode(int) { return (0); } 
Aclass primaryObject = {&Amethode}; 
Aclass* AclassConstructor() { 
Aclass* object; 
object = malloc(sizeof(Aclass)); 
memcpy(object, primaryObject, sizeof(Aclass)); 
return (object); 
} 
+1

你是否聲稱在C++中的成員函數是通過函數指針屬性來實現的?事實並非如此。成員函數是由編譯器靜態定義和靜態解析的。函數指針在使用虛擬函數時進入它,但採用與我認爲你描述的方式不同的方式。 – 2014-09-26 09:07:23

+0

@Magnus Hoff事實上,你是對的,事實並非如此,但對於一個首次亮相的程序員來說,它更容易理解,或者我認爲是這樣...... – CollioTV 2014-09-26 09:11:00

+1

雖然你所描述的方案可行,但它並不代表實際發生的事情因此令人困惑。我認爲以下幾點更容易理解,除了更接近地表示實際發生的情況外:http://ideone.com/4xhr8T – 2014-09-26 09:15:14

8

你並不需要你定義它之前聲明的方法,但是你需要在類中聲明類方法。否則它不會是一個類方法。

這似乎是一個矛盾,但定義也是一個聲明。所以,這是什麼意思是,這一定義可能出現在類本身:

class A { 
    void testMethod() { /*...*/ } 
}; 

[編輯] 此外,實際而言,類聲明中有privateprotectedpublic部分。這是封裝所必需的。如果你可以在類之外聲明方法,你將失去封裝。任何人都可以通過定義額外的獲取者和設置者來訪問私有成員,即使這些成員沒有意義。類不變量將變得毫無意義。

+0

@TonyD你不會降低其他問題以獲得另一個答案,這是你的個人意見,而其他人可能會不同意。現在我明白誰也低估了我的回答。如果答案是錯誤/無用的,那麼你的意圖就是失望,如果你覺得有一個更好的或者你最喜歡的答案,那麼你的意圖就會降低。 – 2014-09-26 10:02:22

+0

「如果你能在課堂外聲明方法,你將失去封裝。」當然,你可以通過在Java的每個方法定義上允許訪問修飾符來解決這個問題。 – 2014-09-27 08:21:26

2

「方法」或「成員函數」(正如C++中更常見的術語)是類聲明的一部分。既然你必須在一個地方聲明一個C++類,你必須確保所有的「成員函數」(或「方法」)已經存在於該聲明中。

我知道它是不是需要申報常見的C方法的方法,但是當你是指C++中的「常見的C法」,他們 正好可以定義

,實際上指的是「共同功能」。請注意,您可以在任何聲明此類函數的地方聲明類。

此外,請注意,您可以聲明一個正文的成員函數。您不必單獨聲明和定義。即這是完全有效的:

class A{ 
    void privateMethod() { 
     // do something here... 
    } 
public: 
    void publicMethod() { 
     // do something here... 
    } 
}; 
2

請注意使用限定符::。這意味着

    你有你有一個命名空間,類或方法/功能標識正確的命名空間或類標識符

所以寫void A::testMethod()假設有一個類或名稱空間A定義 - 這是如何定義C++。這適用於

void A::testMethod(); 

以及爲

void A::testMethod() 
{ 
} 

另外請注意,你必須確實沒有在::左側爲

void ::testMethod() 
{ 
} 

根據定義的全局命名空間全局名稱空間總是被定義的,所以上面的代碼定義了一個類似於沒有限定符的C風格的函數。

2

即使它沒有被標準強制規定,爲什麼需要在類定義中聲明所有的類方法還有以下兩個原因。

  1. 您只能在類聲明中聲明爲public,private或protected,但不能在.cpp文件的方法定義中這樣做。那麼你的獨立式課堂方法有什麼可見性?

  2. 如果標準決定選擇其中一個作爲默認值(C++默認爲私有),並將該可見性放在您的方法上,則您現在有一個範圍問題。即使是最嚴格的可見性(私有),也意味着您可以在任何其他成員方法中使用該方法,包括在源文件之前定義的那些成員方法。如果沒有類定義中的聲明,那些早期的函數將不會意識到您的自由站立方法,因此您違反了範圍規則。

在你的了foo.h頭:

class foo 
{ 
public: 
    foo() {} 
    virtual ~foo() {} 

    declaredMethod(); 
}; 

在你Foo.cpp中

foo::declaredMethod() 
{ 
    ... 
    freeStandingMethod(); // This should be legal, but it can't work since we 
          // haven't seen foo::freeStandingMethod() yet 
    ... 
} 

foo::freeStandingMethod() 
{ 
    ... 
} 

即使你能在同一個.cpp文件這項工作,是合法的將foo::freeStandingMethod()放在與foo::declaredMethod()不同的.cpp文件中,此時這變得不可能。

2

這不是一個答案,但你可能會發現它的信息和樂趣。 添加2個模板功能,你的類將有效地讓你調用任何自由函數,它這個類的一個對象作爲第一個參數:

#include <string> 
#include <iostream> 

struct puppy { 
    puppy(std::string name) 
    : _name(std::move(name)) 
    {} 

    const std::string& name() const noexcept { 
     return _name; 
    } 

    void set_name(std::string name) { 
    _name = std::move(name); 
    } 

    template<class F, class ...Args> 
     auto perform(F&& f, Args&&...args) const 
     -> decltype(f(*this, std::forward<Args>(args)...)) 
    { 
     return f(*this, std::forward<Args>(args)...); 
    } 

    template<class F, class ...Args> 
     auto perform(F&& f, Args&&...args) 
     -> decltype(f(*this, std::forward<Args>(args)...)) 
    { 
     return f(*this, std::forward<Args>(args)...); 
    } 

private: 
    std::string _name; 
}; 


void woof(const puppy& p) { 
    std::cout << "puppy " << p.name() << " woofs!" << std::endl; 
} 

void indented_woof(const puppy&p, size_t indent) { 
    std::cout << std::string(indent, ' '); 
    woof(p); 
} 

void complex_woof(const puppy& p, int woofs) 
{ 
    std::cout << "attention!" << std::endl; 
    for (int i = 0 ; i < woofs ; ++i) { 
    p.perform(indented_woof, 4); 
    } 
} 

std::string name_change(puppy& p, std::string(new_name)) 
{ 
    auto old_name = p.name(); 
    p.set_name(std::move(new_name)); 
    return old_name; 
} 

int main() 
{ 
    puppy fido { "fido" }; 

    fido.perform(woof); 
    fido.perform(complex_woof, 10); 
    auto old_name = fido.perform(name_change, "bonzo"); 
    fido.perform(woof); 
    std::cout << "changed name from " << old_name << std::endl; 
    return 0; 
} 
+0

回答(現在刪除)的評論: 執行的一個版本必須是const以支持採用非常量小狗&的函數。 – 2014-09-26 11:15:30

3

以前所有的答案是正確的,只要他們去,但他們沒有指出規則背後的原因。在C++中, 類定義已關閉;你以後不能添加它。這個 對於非靜態數據成員是必需的,因爲它們決定了大小(以及隱式生成的特殊函數) ,但它是C++中所有類成員的基本原則:不僅僅是 數據,而是函數,類型,對於 好封裝被認爲是必不可少的。

對於支持類概念的大多數(但不是全部)語言都是如此。

這也解釋了爲什麼它的情況不同於 名稱空間(它們沒有關閉)。

+0

+1(早期),但公平性「以前的所有答案」,除Csq's外。 – 2014-09-26 11:16:27

相關問題