繁体   English   中英

学习 C++ 并在将类接口与实现分离时遇到问题

[英]Learning C++ and having a problem correclty separating class interface from implementation

我正在使用 Xcode 学习 C++,并编写了几个小程序,包括一个刽子手游戏,但是每次我尝试将一个类分为定义和实现时都会遇到麻烦。 我做了一个简单的案例来说明我的问题。 简短版本似乎我需要在实现文件中指定一个类型,即使它已经在头文件中定义。 在我的示例中,我在第 12 和 13 行得到“ C++ requires a type specifier for all declarations ”。 但是,例如,如果我将第 12 行更改为

 int xmlelem::atrb_count = 0;

它得到错误“ non-static data member defined out-of-line ”。 在其他情况下,我收到一个错误,说我试图重新定义某些东西。 我想我在某处遗漏了一个基本概念。 在我查看的一些类似问题中,我没有看到这个特定问题。

xmlelem.hpp

//  xmlelem.hpp
//  learn header
//
//

#ifndef xmlelem_hpp
#define xmlelem_hpp

#include <stdio.h>
#include <string>

#endif /* xmlelem_hpp */

class xmlelem {
    
private:
    int atrb_count;
    std::string tag_name;
    
public:
    xmlelem(std::string tag);
    void add_atrib();
    std::string output();
};

xmlelem.cpp

//  xmlelem.cpp
//  learn header
//.
//

#include "xmlelem.hpp"
#include "string"
#include <iostream>

// line 11
xmlelem::atrb_count = 0;
xmlelem::tag_name = "";

xmlelem::xmlelem(std::string tag){
    tag_name = tag;
}

void  xmlelem::add_atrib(){
    atrb_count++;
}

std::string xmlelem::output(){
    std::string build = "<";
    build = build + tag_name + " " + std::to_string(atrb_count);
    build = build + ">";
    return build;
}

main.cpp

//  main.cpp
//  learn header
//
//

#include <iostream>
#include "xmlelem.hpp"
using namespace std;

int main(){
    xmlelem clip("test)");
    std::cout << clip.output() << " test \n";
}

请记住,您正在声明一个类。 类是一个抽象的概念。 当你这样做时xlemem::atrb_count = 0; ,您对抽象概念具有具体价值。 没有意义,对吧? 当您想到狗的一般概念时,您不会想到特定的颜色。 任何初始化都应该在构造函数内部完成,因为只有在构造函数中我们才能创建一个具体的对象

因此,您应该删除初始化这 2 个属性的第 11 行和第 12 行,并且您的构造函数代码应更改为:

xmlelem::xmlelem(std::string tag){
    tag_name = tag;
    atrb_count = 0;
}

请注意,没有必要将字符串初始化为""

让我们来看看(第二个)错误信息。

non-static data member defined out-of-line

错误有两部分:“非静态数据成员”和“定义外”。 这些是不兼容的,因此必须更改其中之一。 此外,应更改其中一个,否则您可能会遇到不同的问题 确定这两个部分中的哪一个适合您的情况。

保持“定义的脱线”

当线

int xmlelem::atrb_count = 0;

在命名空间范围内遇到(即,既不是在函数也不是在类/结构/联合定义中),它是一个外部定义。 这个定义告诉编译器在那个位置为int保留足够的空间。 然后,每当任何xmlelem对象访问atrb_count成员时,它都会访问该特定空间。 所以所有对象共享一个int

但是,此行为对应于静态成员。 为了使声明与实现一致,需要添加关键字static

class xmlelem {
    
private:
    static int atrb_count;
    /* rest of the class definition */
};

保持“非静态”

非静态数据成员存储在类的每个对象中。 每个对象都可以用它的数据副本做它想做的事,而不会影响其他对象。 所以告诉编译器在对象外保留空间是矛盾的。 简单地删除外部定义就足以消除错误消息,但大概您希望初始化发生在某处,对吗?

非静态数据成员的初始化可以在线或在构造函数中完成。 在线移动初始化的示例如下。

class xmlelem {
    
private:
    int atrb_count = 0;
    /* rest of the class definition */
};

这有时是合理的,但声明的目标是将接口与实现分开。 因此,可能不希望头文件中出现初始值0 ,就像上面那样。 另一种方法是将初始值移至构造函数(移至每个构造函数,如果您有多个)。

xmlelem::xmlelem(std::string tag) :
    atrb_count(0),
    tag_name(tag)
{
}

(我还冒昧地将tag_name初始化移动到了初始化列表中。)

请记住,如果您有多个构造函数,则需要在实际使用默认值的每个构造函数中完成此操作(对于例外情况,请考虑“复制构造函数”)。 重复代码是一个缺点; 由您决定收益是否值得付出代价。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM