这期内容当中小编将会给大家带来有关C++ 前置声明的作用有哪些,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
前置声明是C/C++开发中比较常用的技巧,主要用在三种情形:
extern int var1
;;void foo();
,注意类的成员函数无法单独做前置声明;class Foo;
,也可以前置声明模板类:template class<typename T1, int SIZE>Foo;
。如果类包含在名字空间中,需在名字空间内做前置声明:namespace tlanyan {class Foo;};
,而不能这样:class tlanyan::Foo;
。前置声明作用
根据其用途,前置声明的主要作用为:
前两种用途好理解,第三种稍微复杂点,但却是前置声明最重要的用途。其解决类A包含类B,同时类B包含类A的依赖问题。循环依赖一般是设计层面的问题,可通过接口、引入辅助类等手段化解。前置声明也能解决,只是架构上稍微别扭。
不管A和B是否定义在同一个文件中,c++永远无法解决如下形式的循环依赖(后文解释原因):
// file: A.hpp #include "B.hpp" class A { int id; B b; }; // file: B.hpp #include "A.hpp" class B { ... A a; };
前置声明解决该问题需要与指针配合,转换成另一种形式。要点如下:
使用前置声明后,以下是一种可行的解决形式(两个类均使用了前置声明):
// file: A.hpp //3. 移除对B的包含(使用了#pragma once或者#ifndef B_HPP等保护措施则无必要) // 2. 前置声明类B class B; class A { int id; // 1. 成员变量转换成指针 B* b; }; // file: B.hpp // 3. 移除对A的包含(有包含保护则非必要) // 2. 前置声明类A class B { ... // 1. 成员变量转换成指针 A* a; };
深入前置声明
如果你有其他编程语言的经验,会发现c++有点怪异:Java/C#/Python/PHP等语言可以轻松做到循环引用,无需使用类似的前置声明技巧。这不禁让人思考:C++为何必须要用前置声明才能化解?
原因在于C++定义对象有两种方式:一种是A a形式,a即对象,调用成员变量或函数用.,对象在栈中分配;另一种是A* a,a是指针,调用成员变量或函数用->,其指向地址存储实际对象,对象在堆中分配。
分配对象需要知道具体的内存大小,但以下形式我们不能确定类A和类B对象的大小:
class A { B b; }; class B { A a; };
对于这个简单例子,你可以直观认为A和B占用同样的内存,例如1字节,但也可以是2字节,3字节等;根据内存对齐要求,一般是4字节,8字节等。无论哪种情况,编译器无法确定其对象占用内存,便会报错停止编译。所以你应该知道为什么C++永远不应该(不能)这样做了吧?
那为何前置声明加指针的组合能解决循环引用问题的呢?因为正常情况下,数据类型指针在同一机器的编译器里占同样的内存。指针一般是4或者8个字节,对应32和64位指针。用了指针,即使有循环引用,类的大小也能轻易的确定下来。这也是Java/C#/Python/PHP等可以轻松循环引用的原因:这些语言中,对象变量其实都是指针,也意味着对象变量都是引用传递。
如果不移除文件的相互包含,能否省去前置声明呢?答案是不能,原因如下:
上述就是小编为大家分享的C++ 前置声明的作用有哪些了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。