【问题标题】:Using forward declarations for built in datatypes对内置数据类型使用前向声明
【发布时间】:2010-06-07 11:46:59
【问题描述】:

我知道我们将尽可能使用前向声明而不是包含来加快编译速度。

我有这样的课程Person

#pragma once

#include <string>

class Person
{
public:
    Person(std::string name, int age);
    std::string GetName(void) const;
    int GetAge(void) const;
private:
    std::string _name;
    int _age;
};

还有这样的课程Student

#pragma once

#include <string>

class Person;

class Student
{
public:
    Student(std::string name, int age, int level = 0);
    Student(const Person& person);
    std::string GetName(void) const;
    int GetAge(void) const;
    int GetLevel(void) const;
private:
    std::string _name;
    int _age;
    int _level;
};

在 Student.h 中,我有一个前向声明 class Person; 以在我的转换构造函数中使用 Person。美好的。但是我已经完成了#include &lt;string&gt; 以避免在代码中使用std::string 时出现编译错误。这里如何使用前向声明来避免编译错误?有可能吗?

谢谢。

【问题讨论】:

标签: c++ forward-declaration


【解决方案1】:

由于使用string作为

std::string _name;
//^^^^^^^^^ concrete member    

将需要string 的整个结构,因此必须需要声明。你必须#include &lt;string&gt;


string 的声明可以省略,例如:

std::string* _name;
//^^^^^^^^^^ pointer or reference

您可以使用前向声明,但我仍然建议您不要这样做,因为std::string 不是像 Person 或 Student 那样简单的结构类型,而是一个非常复杂的类型,涉及许多模板:

template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
class basic_string { ... };
typedef basic_string<char> string;

如果转发声明错误(如class string;),实际使用时编译会因为类型冲突而失败。

【讨论】:

  • 同理,如果我通过值传递Person,前向声明class Person;不够吗?
  • 其实没有,如果你声明一个函数传值传递A类的实例,或者传值返回,并且不调用任何成员函数在上面,前向声明就是 足够了,因为编译器不需要类的大小。
  • “如果你转发声明错误”...有没有办法正确地做到这一点? AFAIK 不能以符合标准的方式完成,至少,按照stackoverflow.com/a/4801442/2757035 ......而且可能根本没有。但是,如果您在实现定义的基础上找到了适合您的方法,那是什么?
【解决方案2】:

您只能使用带有指针和引用的前向声明(因为它们的大小是固定的,与它们所引用的对象的大小无关)。如果按值使用特定的类,编译器需要它的完整定义(以便知道它的确切大小),因此前向声明是不够的。

因此,如果你这样定义Student

class Student
{
public:
    ...
    Student(const Person person);
    ...
private:
    Person person;
};

上述任何一个班级成员都会强制您在标题中使用#includePerson.h。

您很好理解,前向声明有助于避免编译依赖,从而减少编译时间。但是,这主要涉及用户定义的标头,因为这些标头会经常更改。标准库头文件不会改变,因此在这些情况下节省的空间并不大。

【讨论】:

  • 事实上,GetPerson() 不会强迫你包含 Person.h。编译器不需要在其声明中知道函数返回类型的大小。
【解决方案3】:

您只能使用前向声明进行类型识别,例如在函数/方法指针参数原型中使用声明的类型时。如果您要声明一个成员变量(即 std::string _name;),编译器需要的比前向声明所能提供的要多一点。例如,如果有人执行 sizeof(Student),编译器必须有权访问整个声明才能确定大小。

【讨论】:

  • @Peter, @Amardeep - 如果Student 类中有Person 类的成员,你的意思是这个class Person; 前向声明将不成立吗?
  • 抱歉,我编辑了你的答案而不是我的 :-( 现在回滚了。
  • @bdhar:很抱歉我之前的评论回复......我误解了你的问题。答案是肯定的:如果 Student 有一个 Person 类型的成员,那么您必须包含“Person.h”。
【解决方案4】:

也许还可以考虑使用继承。

#include "Person.h" // i am afraid you need to

class Student : public Person
{
     ...

     protected:
         int _level;
         // no name and age, that is inhertianced from person
         // (if you want to access them from student, you maybe need to make them protected)
 };

【讨论】:

  • 是的。谢谢!但是,这完全是一个不同的话题。我只是设计了这个例子,只是为了让我的查询更清楚。
【解决方案5】:

前向声明用于减少包含和解决循环依赖。但是,当在类的公共或受保护接口中使用转发时,您会将包含正确头文件的负担交给类的用户。 除此之外,STL 中定义的转发类型可能无法按预期工作。例如,std::string 可能被定义为typedef basic_string&lt;char&gt; string;。用namespace std{ class string;} 向前声明它会给你一个编译错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-25
    • 2011-08-28
    相关资源
    最近更新 更多