32

我有两个文件:

File1.cpp
File2.cpp

File1 是我的主类,它具有主要方法,File2.cpp 有一个类调用 ClassTwo,我想在我的 File1.cpp 中创建 ClassTwo 的对象

我将它们编译在一起

g++ -o myfile File1.cpp File2.cpp

但是当我尝试通过

//创建类二对象

ClassTwo ctwo;

它不起作用。

错误是

ClassTwo 未在此范围内声明。

这是我的 main.cpp

#include <iostream>
#include <string>
using namespace std;

int main()
{
//here compile error - undeclare ClassTwo in scope.
ClassTwo ctwo;

//some codes
}

这是我的 File2.cpp

#include <iostream>
#include <string>

using namespace std;

class ClassTwo
{
private:
string myType;
public:
void setType(string);
string getType();
};


void ClassTwo::setType(string sType)
{
myType = sType;
}

void ClassTwo::getType(float fVal)
{
return myType;
}

响应将我的 File2.cpp 拆分为另一个 .h 文件,但如果我声明一个类,我如何将其拆分为另一个 .h 文件,因为我需要维护变量(私有)和函数(公共) 以及如何在 main 方法中将 ClassTwo ctwo 添加到我的 File1.cpp

4

5 回答 5

71

您的代码中的基本问题是什么?

您的代码需要分成接口(.h)和实现(.cpp)。
当您编写类似的内容时,编译器需要查看类型的组成

ClassTwo obj;

这是因为编译器需要为类型的对象保留足够的内存,ClassTwo所以它需要查看ClassTwo. 在 C++ 中执行此操作的最常见方法是将代码拆分为头文件和源文件。
类定义放在头文件中,而类的实现放在源文件中。这样,人们可以轻松地将头文件包含到其他需要查看他们创建的对象的类的定义的源文件中。

为什么我不能简单地将所有代码放在 cpp 文件中并将它们包含在其他文件中?

您不能简单地将所有代码放在源文件中,然后将该源文件包含在其他文件中。C++ 标准要求您可以根据需要多次声明实体,但只能定义一次(一个定义规则(ODR)) . 包含源文件会违反 ODR,因为在包含该文件的每个翻译单元中都会创建实体的副本。

如何解决这个特殊问题?

您的代码应按如下方式组织:

//文件1.h

Define ClassOne 

//文件2.h

#include <iostream>
#include <string>


class ClassTwo
{
private:
   string myType;
public:
   void setType(string);
   std::string getType();
}; 

//文件1.cpp

#include"File1.h"

Implementation of ClassOne 

//文件2.cpp

#include"File2.h"

void ClassTwo::setType(std::string sType)
{
    myType = sType;
}

void ClassTwo::getType(float fVal)
{
    return myType;
} 

//main.cpp

#include <iostream>
#include <string>
#include "file1.h"
#include "file2.h"
using namespace std;

int main()
{

    ClassOne cone;
    ClassTwo ctwo;

    //some codes
}

除了包含头文件之外,还有其他方法吗?

如果您的代码只需要创建指针而不是实际对象,那么您不妨使用前向声明,但请注意,使用前向声明会增加对该类型的使用方式的一些限制,因为编译器将该类型视为不完整类型

于 2012-10-04T18:56:50.140 回答
18

C++(和 C 就此而言)将类型、函数和类的“声明”和“实现”分开。您应该在头文件(.h 或 .hpp)中“声明”所需的类,并将相应的实现放在 .cpp 文件中。然后,当您希望在某处使用(访问)一个类时,您#include 相应的头文件。

例子

ClassOne.hpp:

class ClassOne
{
public:
  ClassOne(); // note, no function body        
  int method(); // no body here either
private:
  int member;
};

ClassOne.cpp:

#include "ClassOne.hpp"

// implementation of constructor
ClassOne::ClassOne()
 :member(0)
{}

// implementation of "method"
int ClassOne::method()
{
  return member++;
}

主.cpp:

#include "ClassOne.hpp" // Bring the ClassOne declaration into "view" of the compiler

int main(int argc, char* argv[])
{
  ClassOne c1;
  c1.method();

  return 0;
}
于 2012-10-04T19:01:07.870 回答
2

如果您不想要标题,则需要转发声明类的名称:

class ClassTwo;

重要提示:这仅在某些情况下有效,有关更多信息,请参阅 Als 的答案。

于 2012-10-04T19:01:40.103 回答
2

同时编译两个.cpp文件的事情,并不意味着他们“知道”彼此。您将必须创建一个文件,“告诉”您的File1.cpp,实际上有像ClassTwo这样的函数和类。该文件称为头文件,通常不包含任何可执行代码。(也有例外,例如内联函数,但一开始就忘记它们)它们服务于声明性需求,只是为了告诉哪些函数可用。

当您拥有您的File2.cpp并将其包含在您File1.cpp的.File1.cppFile2.cpp

因此,您应该创建一个头文件,例如File1.hppor File1.h(其他名称也可以,但这只是标准)。它的工作原理如下:

//文件1.cpp

void SomeFunc(char c) //Definition aka Implementation
{
//do some stuff
}

//文件1.hpp

void SomeFunc(char c); //Declaration aka Prototype

对于干净的代码,您可以在顶部添加以下内容File1.cpp

#include "File1.hpp"

以下是围绕File1.hpp的代码:

#ifndef FILE1.HPP_INCLUDED
#define FILE1.HPP_INCLUDED
//
//All your declarative code
//
#endif

这使你的头文件更干净,关于重复的代码。

于 2012-10-04T19:23:18.140 回答
1

当您想将代码转换为结果(可执行文件、库或其他)时,有 2 个步骤:
1)编译
2)链接
在第一步中,编译器现在应该了解您使用的对象大小、函数原型以及可能遗产。另一方面,链接器希望在您的代码中找到函数和全局变量的实现。

现在,当您ClassTwoFile1.cpp编译器中使用时,您对此一无所知,也不知道应该为它分配多少内存,或者例如它具有的女巫成员,或者它是一个类和枚举,甚至是 int 的 typedef,所以编译将失败编译器。添加File2.cpp解决了寻找实现但编译器仍然不满意的链接器的问题,因为它对您的类型一无所知。

所以请记住,在编译阶段你总是只使用一个文件(当然还有那个文件包含的文件),而在链接阶段你需要多个包含实现的文件。并且由于 C/C++ 是静态类型的,并且它们允许它们的标识符用于多种用途(定义、typedef、枚举类、...),因此您应该始终向编译器标识您的标识符,然后使用它,并且作为规则编译器应该总是知道你的变量的大小!

于 2012-10-04T20:36:53.440 回答