C++前置声明的理解

这篇具有很好参考价值的文章主要介绍了C++前置声明的理解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

知识补充

C/C++中引入一个头文件时,在编译器预处理的时候会将引入头文件的地方简单替换成头文件的内容。这样做的后果是很容易引起头文件的重复引用。所以我们在编写头文件是一般有以下规定来防止头文件被重复包含。
MyWidget.h

#ifndef MyWidget_H_
#define MyWidget_H_

#endif

编译器在编译时仅仅会编译.cpp的文件。

在C\C++中对于类或者结构体的大小总是确定了的,如果类或者结构体的大小无法确定那么编译就无法通过。如
Student.h

#ifndef Student_H_
#define Student_H_

#include <string>

class Student{
public:	
	Student();
	Student(std::string name);
	~Student();
	void PrintName();
private:
	std::string m_name;
};

#endif

此时假设我们要为类Student添加一个私有的成员变量是一个结构体类型的如下

struct Family
{
std::string familyName;
int count;
};

情况一

#ifndef Student_H_
#define Student_H_

#include <string>

/*
struct Family
{
std::string familyName;
int count;
};
*/


class Student{
public:	
	Student();
	Student(std::string name);
	~Student();
	void PrintName();
private:
	std::string m_name;
	Family m_family;
};

#endif

编译器直接报错 未定义的标识符 Family。因为此时编译器编译时找不到 Family 结构体的定义。无法确定类 Student 的大小所以最终导致编译失败。
情况二

#ifndef Student_H_
#define Student_H_

#include <string>

/*
struct Family
{
std::string familyName;
int count;
};
*/

struct Family;


class Student{
public:	
	Student();
	Student(std::string name);
	~Student();
	void PrintName();
private:
	std::string m_name;
	Family* m_family;
};

#endif

编译通过,因为此时成员变量 family 是一个Family类型的指针。它的大小是确定的。一般在32位的机器上是4个字节,在64位的机器上是8个字节。

循环依赖

假设有一个极端的场景 有一个 Student 类 有一个成员变量是 Teacher对象。有一个 Teacher 类有一个成员变量是 Student对象。
那么代码如下
Student.h

#ifndef Student_H_
#define Student_H_

#include <string>
#include "Teacher.h"


class Student{
public:	
	Student();
	Student(const std::string& name);
	~Student();
	void PrintName();
private:
	std::string m_name;
	Teacher m_teacher;
};

#endif

Student.cpp

#include "Student.h"
#include <iostream>

Student::Student():m_name("")
{
	
}

Student::Student(const std::string& name)
{
	m_name = name;
}

Student::~Student()
{

}

void Student::PrintName()
{
	std::cout << "Student Name : " << m_name << std::endl;
}

Teacher.h

#ifndef Teacher_H_
#define Teacher_H_

#include <string>
#include "Student.h"

class Teacher {
public:
	Teacher();
	Teacher(const std::string& name);
	~Teacher();
	void PrintName();
private:
	std::string m_name;
	Student m_student;
};

#endif

Teacher.cpp

#include "Teacher.h"
#include <iostream>

Teacher::Teacher():m_name("")
{

}

Teacher::Teacher(const std::string& name)
{
	m_name = name;
}

Teacher::~Teacher()
{

}

void Teacher::PrintName()
{
	std::cout << "Teacher Name : " << m_name << std::endl;
}

假设编译器先编译 Student.cpp文件那么预处理后
Student.cpp内容为
替换了 Student.h 和 Teacher.h后

#ifndef Student_H_
#define Student_H_

#include <string>
#ifndef Teacher_H_
#define Teacher_H_

#include <string>
#include "Student.h"

class Teacher {
public:
	Teacher();
	Teacher(const std::string& name);
	~Teacher();
	void PrintName();
private:
	std::string m_name;
	Student m_student;
};

#endif


class Student {
public:
	Student();
	Student(const std::string& name);
	~Student();
	void PrintName();
private:
	std::string m_name;
	Teacher m_teacher;
};

#endif
#include <iostream>

Student::Student():m_name("")
{
	
}

Student::Student(const std::string& name)
{
	m_name = name;
}

Student::~Student()
{

}

void Student::PrintName()
{
	std::cout << "Student Name : " << m_name << std::endl;
}

此时在去替换#include "Student.h" 因为有头文件包含机制将无事发生,此时 Teacher 类无法知道 成员变量 student的大小编译失败。修改为前置声明后
Student.h

#ifndef Student_H_
#define Student_H_

#include <string>
class Teacher;


class Student{
public:	
	Student();
	Student(const std::string& name);
	~Student();
	void PrintName();
private:
	std::string m_name;
	Teacher* m_teacher;
};
#endif

Teacher.h

#ifndef Teacher_H_
#define Teacher_H_

#include <string>
class Student;

class Teacher {
public:
	Teacher();
	Teacher(const std::string& name);
	~Teacher();
	void PrintName();
private:
	std::string m_name;
	Student* m_student;
};

#endif

此时可以编译通过。这是因为在Student类中没有使用Teacher类的对象。在Tacher类中也没有使用到Student类的对象。使用的情况如下。
Student.cpp

#include "Student.h"
#include <iostream>
#include "Teacher.h"

Student::Student():m_name("")
{
	m_teacher = new Teacher("laoWang");
}

Student::Student(const std::string& name)
{
	m_name = name;
	m_teacher = new Teacher("laoWang");
}

Student::~Student()
{

}

void Student::PrintName()
{
	std::cout << "Student Name : " << m_name << std::endl;
}

Teacher.cpp

#include "Teacher.h"
#include <iostream>
#include "Student.h"

Teacher::Teacher():m_name("")
{
	m_student = new Student("xiaoMing");
}

Teacher::Teacher(const std::string& name)
{
	m_name = name;
	m_student = new Student("xiaoMing");
}

Teacher::~Teacher()
{

}

void Teacher::PrintName()
{
	std::cout << "Teacher Name : " << m_name << std::endl;
}

main.cpp

#include <iostream>
#include "Student.h"

int main()
{
	Student xiaoMing("xiaoMing");
	xiaoMing.PrintName();
	return 0;
}

此时在visual stdio 2015中运行程序发现什么东西都没有
运行结果
C++前置声明的理解
这是因为又有一个new的循环导致内存被撑爆了。

Student xiaoMing("xiaoMing");

Student的构造函数

Student::Student():m_name("")
{
	m_teacher = new Teacher("laoWang");
}

Teacher.cpp

Teacher::Teacher():m_name("")
{
	m_student = new Student("xiaoMing");
}

这个是无解的所以不能出现,A类包含B类的成员变量,B类包含A类的成员变量。同时它们又在构造函数中给成员变量赋值。
解决方案是只允许某一个类去调用另一个类的方法,不允许相互调用。

最终代码

代码下载PointerTest文章来源地址https://www.toymoban.com/news/detail-438970.html

到了这里,关于C++前置声明的理解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ts解决依赖引入报错:无法找到模块“xxxxxx”的声明文件的报错问题

    依赖引入报错是因为 ts 没有识别当前引入的依赖,在 vite-env.d.ts 中声明该依赖即可解决,语法: declare module \\\"依赖名\\\"; 解决找不到模块“./App.vue”或其相应的类型声明。 解决router引入报错的问题 一些依赖报错问题的解决

    2024年02月11日
    浏览(43)
  • 解决vue3+vite项目中引入mockjs失败的问题--无法找到模块“mockjs”的声明文件

     看到上面报错,根据提示 修改声明方式 declare module \\\'mockjs\\\'  我们修改一下引入的声明,发现修改之后仍然报错;    解决方法: 需要在vite-env.d.ts文件中,添加  declare module \\\'mockjs\\\',保存即可  然后就可以正常使用了  

    2024年02月11日
    浏览(43)
  • vscode 中引入文件报类型错误:找不到模块“@/views/login/index.vue”或其相应的类型声明。ts(2307)没有可用的快速修复

    这个错误通常是因为 TypeScript 在编译时无法找到指定路径下的模块或类型声明文件。在这个例子中,它无法找到  @/views/login/index.vue  模块或其相应的类型声明。 请检查你的代码,确保指定路径下的文件存在并且已经正确导出。如果你使用了别名(例如  @ )来指定路径,也

    2024年02月15日
    浏览(36)
  • 【C++精华铺】6.C++类和对象(下)类与对象的知识补充及编译器优化

    目录 1. 再谈构造 1.1 成员变量的初始化(初始化列表) 1.2 初始化列表的行为 1.3 explicit  2. 类中的static成员 2.1 静态成员变量 2.2 静态成员函数 3. 友元 3.1 友元函数 3.1 友元类 4. 内部类  5. 匿名对象  6. 对象拷贝时候的编译器优化           为什么还要去看初始化的问

    2024年02月13日
    浏览(37)
  • 深入理解 C++ 语法:从基础知识到高级应用

    让我们将以下代码分解以更好地理解它: 示例 示例解释 第 1 行: #include iostream 是一个头文件库,它让我们可以使用输入和输出对象,比如 cout (在第 5 行使用)。头文件为 C++ 程序添加功能。 第 2 行: using namespace std 表示我们可以使用标准库中的对象和变量名称。 如果你

    2024年03月23日
    浏览(43)
  • (一) AIGC了解+前置知识

    大论文双盲意见还没回来,每天度日如年,慌的一批,唯恐延毕,得找点事情干~ 小论文major revision,本来打算一鼓作气把小论文完全改好的,但是搞了三个月的文字工作,好久没有吸收新知识了 所以…每天边学新东西,边改小论文~ 最近AIGC比较火,就从它开始吧 AIGC大致了解

    2024年02月13日
    浏览(28)
  • 7.前置知识3:LoadBalance

    https://medium.com/google-cloud/understand-cloud-load-balancer-like-a-senior-engineer-d4f55f3111fc 负载均衡本来是个硬件设备。其实一开始确实是的,然而现在已经不同了。 尤其是云厂商提供的负载均衡方案几乎全部是靠软件。现在的负载均衡不仅是网络流量复杂均衡,几乎所有的平衡多个计算资

    2024年02月20日
    浏览(44)
  • JUC前置知识

    JUC概述 在开发语言中,线程部分是重点,JUC是关于线程的。JUC是java.util.concurrent工具包的简称。这是一个处理线程的工具包,JDK1.5开始出现的。 线程和进程 线程和进程的概念 进程(process): 是计算机的程序关于某数据集合上的一次允许活动,是操作系统进行资源分配和任务调

    2024年02月08日
    浏览(38)
  • C#代码审计实战+前置知识

    菜鸟教程:https://www.runoob.com/csharp/csharp-intro.html C# 基于 C 和 C++ 编程语言,是一个简单的、现代的、通用的、面向对象的编程语言,它是由微软(Microsoft)开发的,由 Ecma 和 ISO 核准认可的。 C# 是由 Anders Hejlsberg 和他的团队在 .Net 框架开发期间开发的。 C# 是专为公共语言基础

    2024年02月05日
    浏览(37)
  • 前置知识——Linux网络虚拟化

    信息是如何通过网络传输被另一个程序接收到的? 我们讨论的虚拟化网络是狭义的,它指容器间网络。 好了,下面我们就从 Linux 下网络通信的协议栈模型,以及程序如何干涉在协议栈中流动的信息来开始了解吧。 如果抛开虚拟化,只谈网络的话,那我认为首先应该了解的知

    2023年04月12日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包