C++程序结构

14 浏览
0 Comments

C++程序结构

我在C++中如何构建程序的结构有些困惑。我正在学习C++的一本书,其中我们构建了两个类来解决一个问题。书中把这两个类以及它们的使用方式都放在一个文件中运行,这样可以正常工作。但我知道更合理结构的代码应该包括头文件,并且每个类都应该有自己的文件,当我尝试按照这种方式组织程序时,就会在编译代码时出现问题。\n我有两个类,Token和Token_Stream,根据我了解的其他编程语言,Token和Token_Stream应该有各自的文件,并且每个类都应该有一个声明文件。我的主要问题是:\nToken_Stream需要知道Token的存在。当初始化Token_Stream时,它会初始化一个Token。我原以为在Token_Stream中只需要包含Token的声明就足够了,但实际情况似乎并非如此。我对面向对象编程语言有一些了解,但Token_Stream不应该继承自Token,也不应该(我认为)它只需要知道足够的信息来初始化Token并存储它。我将在下面包含每个相关文件:\nToken.h\n

// Token.h, Token的声明
class Token
{
public:
    char kind;
    double value;
    Token(char ch);
    Token(char ch, double val);
}; //class Token

\nToken.cpp\n

// Token.cpp
#include "Token.h"
using namespace std;
Token::Token(char ch) 
    :kind(ch), value(0){}
Token::Token(char ch, double val)
    :kind(ch), value(val) {}

\nToken_Stream.h\n

// Token_Stream.h, Token_Stream的声明
class Token_Stream
{
public:
    Token_Stream();
    Token get();
    void putback(Token);
private:
    bool full; // 是否已有一个token?
    Token buffer; // 我们持有的Token是什么?
};//class Token_Stream

\nToken_Stream.cpp\n

// Token_Stream.cpp实现
#include 
#include 
#include "Token.h" // 需要知道什么是Token
#include "Token_Stream.h"
using namespace std;
Token_Stream::Token_Stream()
:buffer(0)
{
    full = false; // 流中还没有任何内容。
}//constructor
void Token_Stream::putback(Token t)
{
    if(!full) // 如果它是空的
    {
        buffer = t;
        full = true;
    }//if not full
    else
        throw runtime_error("buffer already full");
}// putback
Token Token_Stream::get()
{
    if(full) // 如果我们已经有了东西
    {
        full = false;
        return buffer;
    }
    // 如果到达这里,我们还没有返回:
    char ch;
    cin>>ch; //获取下一个输入并切换到不同的情况:
    switch(ch)
    {
        // 如果输入一个有效字符:
        case ';': 
        case 'q':
        case '(': case '+': case '*': case '-': case '/': case '%': 
        case ')':
            return Token(ch);
            break;
        // 如果输入一个有效数字,或者以小数点开头,例如,.5
        case '.': case '0': case '1': case '2': case '3': case '4': 
        case '5': case '6': case '7': case '8': case '9': 
        {
            cin.putback(ch);
            double val;
            cin>>val; //将其读取为数字
            return Token('8',val);
            break;
        }//case of valid number
        default:
            throw runtime_error("Bad Token");
    }//switch
}//get

\n这些就是文件,当我尝试编译时,例如在Token.cpp中放入一个空的int main(){},一切都正常工作,我编译,如果需要可以在main()中运行代码。\n但是,当我在Token_Stream.cpp中放入一个空的int main(){}并尝试编译时,它不起作用,我运行的是:\n

g++ -Wall -std=c++11 -o "Token_Stream" "Token_Stream.cpp"

\n我甚至没有得到行号错误,但它声称对Token::Token(char)等的引用未定义,以及其他Token构造函数,所以我猜这意味着Token_Stream.cpp需要看到更多的Token.cpp,我该如何做?我只需要同时编译它们吗?

0
0 Comments

C++程序结构问题的出现原因是在处理链接问题的同时,还需要解决程序内部的依赖关系。

解决方法如下:

Token_Stream.h文件应该以以下方式开始:

// Token_Stream.h, declarations
#include "Token.h"  // 注意这个包含语句在文件顶部
class Token_Stream
...

Token_Stream.cpp文件应该以以下方式开始:

// Token_Stream.cpp implementation.
#include "Token_Stream.h"  // 注意这个包含语句在文件顶部
#include 
#include 
...

上述主要要点是:

  1. 每个头文件应该在其实现文件的顶部被包含。
  2. 每个头文件应该只包含它实际需要的内容。

这些前提条件将使得客户端能够在需要的地方包含你的头文件。

0
0 Comments

原因:使用C++编译器编译一个C++程序时,如果程序中引用了其他文件中的函数或类,需要将这些文件链接到可执行文件中。在这个例子中,程序中引用了Token.cpp中的构造函数,但是没有将Token.cpp链接到可执行文件中。

解决方法:在编译命令中加入需要链接的文件,即将Token.cpp链接到可执行文件中。

文章内容如下:

你需要将Token.cpp链接到你的可执行文件中。

g++ -Wall -std=c++11 -o "Token_Stream" "Token.cpp" "Token_Stream.cpp"

否则,gcc找不到Token的构造函数的实现。

啊!好的,这样确实可以编译了!谢谢。所以这个想法是,如果你在另一个文件中有实现,你也必须将它给编译器,编译器会自动解决依赖关系?或者这里的顺序很重要吗?

实际上是链接器(linker)而不是编译器在做这些事情。是的,链接器会为你解决这些依赖关系(大部分情况下)。顺序可能很重要,但是暂时不用担心;这更多是与使用-l标志链接的第三方库有关的问题。

当然了。是的,-o是给链接器用的,对吗?

0