[英]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.