实验于华中农业大学逸夫楼2017.3.10

在编写C++程序的时候,偶尔需要用到前置声明(Forward declaration)。下面的程序中,带注释的那行就是类B的前置说明。这是必须的,因为类A中用到了类B,而类B的声明出现在类A的后面。如果没有类B的前置说明,下面的程序将不同通过编译,编译器将会给出类似“缺少类型说明符”这样的出错提示。

代码一:

// ForwardDeclaration.h

#include <iostream>

using namespace std;

class B;             // 这是前置声明(Forward declaration)

class A

{

private:

B* b;

public:

A(B* b):b(b)

{

}

};

class B

{

};

// Main.cpp

#include "ForwardDeclaration.h"

int main(int argc, char** argv)

{

B* b = new B();

A* a = new A(b);

delete a;

delete b;

return 0;

}

上面程序可以顺利编译和运行(几乎没有做什么,也没有输出)。

是不是有了前置说明就万事大吉了呢?我们看看下面的代码(带阴影部分的代码是新增加的):

代码二:

// ForwardDeclaration.h

#include <iostream>

using namespace std;

class B;             // 这是前置声明(Forward declaration)

class A

{

private:

B* b;

public:

A(B* b):b(b)

{

}

void someMethod()

{

b->someMethod();                                                  // (1)

}

};

class B

{

private:

public:

void someMethod()

{

cout << "something happened..." << endl;

}

};

// Main.cpp

#include "ForwardDeclaration.h"

int main(int argc, char** argv)

{

B* b = new B();

A* a = new A(b);

a->someMethod();

delete a;

delete b;

return 0;

}

一编译,发现代码(1)处出错。出错提示往往包括(不同的编译器给出的提示会有所不同):

  1. 使用了未定义的类型B;
  2. “->somemethod”的左边必须指向类/结构/联合/泛型类型

原因:

  1. (1)处使用了类型B的定义,因为调用了类B中的一个成员函数。前置声明class B;仅仅声明了有一个B这样的类型,而并没有给出相关的定义,类B的相关定义,是在类A后面出现的,因此出现了编译错误;
  2. 代码一之所以能够通过编译,是因为其中仅仅用到B这个类型,并没有用到类B的定义。

解决办法是什么?

将类的声明和类的实现(即类的定义)分离。如下所示:

// ForwardDeclaration.h   类的声明

#include <iostream>

using namespace std;

class B;             // 这是前置声明(Forward declaration)

class A

{

private:

B* b;

public:

A(B* b);

void someMethod();

};

class B

{

private:

public:

void someMethod();

};

// ForwardDeclaration.cpp        类的实现

#include "ForwardDeclaration.h"

A::A(B* b):b(b)

{

}

void A::someMethod()

{

b->someMethod();

}

void B::someMethod()

{

cout << "something happened..." << endl;

}

// Main.cpp

#include "ForwardDeclaration.h"

int main(int argc, char** argv)

{

B* b = new B();

A* a = new A(b);

a->someMethod();

delete a;

delete b;

return 0;

}

结论:

前置声明只能作为指针或引用,不能定义类的对象,自然也就不能调用对象中的方法了。

而且需要注意,如果将类A的成员变量B* b;改写成B& b;的话,必须要将b在A类的构造函数中,采用初始化列表的方式初始化,否则也会出错。

文章来源:http://patmusing.blog.163.com/blog/static/135834960201038113714199/

关于C++中的前置声明(附程序运行图)的更多相关文章

  1. c++中的前置声明

    引用google c++编码规范: When you include a header file you introduce a dependency that will cause your cod ...

  2. c++中的namespace(附程序运行图)

    实验于华中农业大学逸夫楼2017.3.10 namespace中文意思是命名空间或者叫名字空间,传统的C++只有一个全局的namespace,但是由于现在的程序的规模越来越大,程序的分工越 来越细,全 ...

  3. C++中前置声明介绍

    前置声明是指对类.函数.模板或者结构体进行声明,仅仅是声明,不包含相关具体的定义.在很多场合我们可以用前置声明来代替#include语句. 类的前置声明只是告诉编译器这是一个类型,但无法告知类型的大小 ...

  4. C++中头文件相互包含与前置声明

    一.类嵌套的疑问 C++头文件重复包含实在是一个令人头痛的问题,前一段时间在做一个简单的数据结构演示程序的时候,不只一次的遇到这种问题.假设我们有两个类A和B,分别定义在各自的有文件A.h和B.h中, ...

  5. C++中前置声明的应用与陷阱

    前置声明的使用 有一定C++开发经验的朋友可能会遇到这样的场景:两个类A与B是强耦合关系,类A要引用B的对象,类B也要引用类A的对象.好的,不难,我的第一直觉让我写出这样的代码: // A.h #in ...

  6. 【原创】SystemVerilog中的typedef前置声明方式

    SystemVerilog中,为了是代码简洁.易记,允许用户根据个人需要使用typedef自定义数据类型名,常用的使用方法可参见"define和typedef区别".但是在Syst ...

  7. 编写Java程序,模拟五子棋博弈过程中的异常声明和异常抛出

    返回本章节 返回作业目录 需求说明: 模拟五子棋博弈过程中的异常声明和异常抛出,判断用户所下棋子的位置,是否超越了棋盘的边界. 棋盘的横坐标的范围为0-9,纵坐标范围为0-14,如果用户所放棋子的坐标 ...

  8. [转]从普通DLL中导出C++类 – dllexport和dllimport的使用方法(中英对照、附注解)

      这几天写几个小程序练手,在准备将一个类导出时,发现还真不知道如果不用MFC的扩展DLL,是怎么导出的.但我知道dllexport可以导出函数和变量,而且MFC扩展DLL就算是使用了MFC的功能,但 ...

  9. C++ 类声明 类前置声明范例

    转载自http://www.cnblogs.com/staring-hxs/p/3244251.html 在编写C++程序的时候,偶尔需要用到前置声明(Forward declaration).下面的 ...

随机推荐

  1. 【转】Hibernate和IBatis对比

    原文地址:http://blog.csdn.net/ya2dan/article/details/7396598 项目也做过几个, 使用IBatis就做一个项目, 基本上都是使用Hibernate, ...

  2. 转:KVC与KVO机制

    由于Objective-C是基于Smalltalk进行设计的,所以它具有动态加载.动态绑定等特性.Key-value coding (KVC) 和 key-value observing (KVO) ...

  3. Linux内存中的 buffer 和 cache 到底是个什么东东?

    Linux 中的 free 命令,会输出: total 总量 used  已使用 free 空闲 shared 共享内存 buffers cached 前面四项都比较好理解,一看我也就知道啥意思了.但 ...

  4. [ZooKeeper.net] 1 模仿dubbo实现一个简要的http服务的注册 基于webapi

    今天来试着模仿下dubbo实现一个简要的http服务的注册,虽说是模仿不过是很廉价的那种,只是模仿了一点点点...... 先放上demo目录结构: 开头还是把ZooKeeper的一些简要介绍搬过来看看 ...

  5. 移动端利用rem实现自适应布局

    好久没有写博客了,刚好说说最近遇到的移动端布局问题吧. 本来一直是觉得我的页面布局能力还是不错的,当然,是相对于较基础的来说还是不错的.不过,自己写的案例终归是跟实际开发有区别的,自己写案例的是觉得这 ...

  6. 深入理解ajax系列第三篇——响应解码

    前面的话 我们接收到的响应主体类型可以是多种形式的,包括字符串String.ArrayBuffer对象.二进制Blob对象.JSON对象.javascirpt文件及表示XML文档的Document对象 ...

  7. 1578: [Usaco2009 Feb]Stock Market 股票市场

    1578: [Usaco2009 Feb]Stock Market 股票市场 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 414  Solved: 1 ...

  8. 网络AFNetworking 3.1

    下面的类已从AFNetworking 3.0中废弃: AFURLConnectionOperation AFHTTPRequestOperation AFHTTPRequestOperationMan ...

  9. 将两个列不同的DataTable合并成一个新的DataTable

    /// <summary>         /// 将两个列不同(结构不同)的DataTable合并成一个新的DataTable         /// </summary> ...

  10. 有个程序猿要去当CEO了:(二)扬帆起航

    合同签好了. 从昨天下午三点半,一直修改到晚上七点半,才确定签下. 这过程中,有一点讨论得比较久: 就是甲方要不要也拿底薪. 甲方是这样说的:"总经理拿N仟元,董事长不要说比总经理高,但是也 ...