2014-09-26 212 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

相关问题