学习 C++ 并在正确地将 class 接口与实现分开时遇到问题
Learning C++ and having a problem correclty separating class interface from implementation
我正在使用 Xcode 学习 C++ 并编写了几个小程序,包括 hangman 游戏,但每次我尝试将 class 分为定义和实现时,我都会遇到麻烦。我做了一个简单的案例来说明我的问题。简短版本似乎我需要在实现文件中指定一个类型,即使它已经在头文件中定义了。在我的示例中,我在第 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";
}
请记住,您要声明的是 class。 A class 是一个抽象概念。当您这样做 xlemem::atrb_count = 0;
时,您对抽象概念具有具体价值。没有道理,对吧?当您想到狗的一般概念时,您不会想到特定的颜色。任何初始化都应该在构造函数内部完成,因为只有在构造函数中我们才能创建具体的 object.
因此,您应该删除初始化这 2 个属性的第 11 行和第 12 行,并且您的构造函数代码应更改为:
xmlelem::xmlelem(std::string tag){
tag_name = tag;
atrb_count = 0;
}
请注意,不必将字符串初始化为 ""
。
让我们看一下(第二个)错误信息。
non-static data member defined out-of-line
错误分为两部分:“non-static 数据成员”和“已定义 out-of-line”。这些是不兼容的,因此必须更改其中之一。另外,只能其中一个改,否则可能运行变成a different problem。确定这两部分中哪一部分适合您的情况。
保持“定义out-of-line”
当行
int xmlelem::atrb_count = 0;
在 namespace 作用域(即既不是函数也不是 class/struct/union 定义中)遇到,它是一个 out-of-line 定义。这个定义告诉编译器在那个地方为 int
保留足够的 space。然后,每当任何 xmlelem
对象访问 atrb_count
成员时,它都会访问这个特定的 space。所以有一个 int
被所有对象共享。
但是,此行为对应于静态成员。为了使声明与实现一致,需要添加关键字static
。
class xmlelem {
private:
static int atrb_count;
/* rest of the class definition */
};
保留“non-static”
non-static 数据成员存储在 class 的每个对象中。每个对象都可以用它的数据副本做它想做的事,而不会影响其他对象。所以告诉编译器在对象外保留 space 是自相矛盾的。只需删除 out-of-line 定义就足以消除错误消息,但大概您希望在某处进行初始化,对吗?
non-static 数据成员的初始化可以在 in-line 或构造函数中完成。移动初始化 in-line 的示例如下。
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
的初始化移到了 initialization list 中。)
请记住,如果您有多个构造函数,则需要在每个实际使用默认值的构造函数中执行此操作(对于例外情况,请考虑“复制构造函数”)。重复代码是缺点;由您决定收益是否值得付出代价。
我正在使用 Xcode 学习 C++ 并编写了几个小程序,包括 hangman 游戏,但每次我尝试将 class 分为定义和实现时,我都会遇到麻烦。我做了一个简单的案例来说明我的问题。简短版本似乎我需要在实现文件中指定一个类型,即使它已经在头文件中定义了。在我的示例中,我在第 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";
}
请记住,您要声明的是 class。 A class 是一个抽象概念。当您这样做 xlemem::atrb_count = 0;
时,您对抽象概念具有具体价值。没有道理,对吧?当您想到狗的一般概念时,您不会想到特定的颜色。任何初始化都应该在构造函数内部完成,因为只有在构造函数中我们才能创建具体的 object.
因此,您应该删除初始化这 2 个属性的第 11 行和第 12 行,并且您的构造函数代码应更改为:
xmlelem::xmlelem(std::string tag){
tag_name = tag;
atrb_count = 0;
}
请注意,不必将字符串初始化为 ""
。
让我们看一下(第二个)错误信息。
non-static data member defined out-of-line
错误分为两部分:“non-static 数据成员”和“已定义 out-of-line”。这些是不兼容的,因此必须更改其中之一。另外,只能其中一个改,否则可能运行变成a different problem。确定这两部分中哪一部分适合您的情况。
保持“定义out-of-line”
当行
int xmlelem::atrb_count = 0;
在 namespace 作用域(即既不是函数也不是 class/struct/union 定义中)遇到,它是一个 out-of-line 定义。这个定义告诉编译器在那个地方为 int
保留足够的 space。然后,每当任何 xmlelem
对象访问 atrb_count
成员时,它都会访问这个特定的 space。所以有一个 int
被所有对象共享。
但是,此行为对应于静态成员。为了使声明与实现一致,需要添加关键字static
。
class xmlelem {
private:
static int atrb_count;
/* rest of the class definition */
};
保留“non-static”
non-static 数据成员存储在 class 的每个对象中。每个对象都可以用它的数据副本做它想做的事,而不会影响其他对象。所以告诉编译器在对象外保留 space 是自相矛盾的。只需删除 out-of-line 定义就足以消除错误消息,但大概您希望在某处进行初始化,对吗?
non-static 数据成员的初始化可以在 in-line 或构造函数中完成。移动初始化 in-line 的示例如下。
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
的初始化移到了 initialization list 中。)
请记住,如果您有多个构造函数,则需要在每个实际使用默认值的构造函数中执行此操作(对于例外情况,请考虑“复制构造函数”)。重复代码是缺点;由您决定收益是否值得付出代价。