C++前置声明的理解

news2024/11/25 13:40:06

知识补充

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中运行程序发现什么东西都没有
运行结果
在这里插入图片描述
这是因为又有一个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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/480514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实验四、彩色图像处理

实验目的 使用MatLab软件对图像进行彩色处理&#xff0c;熟悉使用MatLab软件进行图像彩色处理的有关方法&#xff0c;并体会到图像彩色处理技术以及对图像处理的效果。 作业1&#xff1a;生成一副256*256的RGB图像&#xff0c;使得该图像左上角为黄色或者青色&#xff0c;左下…

day04_基本数据类型丶变量丶类型转换

前置知识 计算机世界中只有二进制。那么在计算机中存储和运算的所有数据都要转为二进制。包括数字、字符、图片、声音、视频等。 进制 进制也就是进位计数制&#xff0c;是人为定义的带进位的计数方法 。不同的进制可以按照一定的规则进行转换。 进制的分类 十进制&#x…

Seurat -- Perform linear dimensional reduction

brief 什么是线性降维&#xff1f; 这里是一个很形象的网页演示&#xff0c;其中包括了一个视频链接。 这里是如何用R 包psych做线性降维的演示&#xff0c;其中也有原理的简述。 为什么要做线性降维&#xff1f; 因为下一步的聚类分析需要这里的降维结果作为输入。降维做的好…

14-3-进程间通信-消息队列

前面提到的管道pipe和fifo是半双工的&#xff0c;在某些场景不能发挥作用&#xff1b; 接下来描述的是消息队列&#xff08;一种全双工的通信方式&#xff09;&#xff1b; 比如消息队列可以实现两个进程互发消息&#xff08;不像管道&#xff0c;只能1个进程发消息&#xff…

vulnhub靶机Misdirection

环境准备 下载链接&#xff1a;https://download.vulnhub.com/misdirection/Misdirection.zip 解压后双击ovf文件导入虚拟机 网络&#xff1a;DHCP、NAT、192.168.100.0/24网段 信息收集 主机发现 192.168.100.133是新增的ip 端口扫描 发现开放了以上端口&#xff0c;继续…

【Java笔试强训 28】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;猴子分桃…

【Python入门】Python搭建编程环境-安装Python3解释器(内含Windows版本、MacOS版本、Linux版本)

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于Python零基础入门系列&#xff0c;本专栏主要内容为Python基础语法、判断、循环语句、函…

与其焦虑被 AI 取代或猜测前端是否已死, 不如看看 vertical-align 扎实你的基础!!!

与其焦虑被 AI 取代或猜测前端是否已死, 不如看看 vertical-align 扎实你的基础!!! vertical-align 设置 display 值为 inline, inline-block 和 table-cell 的元素竖直对齐方式. 从 line-height: normal 究竟是多高说起 我们先来看一段代码, 分析一下为什么第二行的行高, 也就…

D. Mysterious Crime(单个位置贡献)

Problem - D - Codeforces Acingel是一个小镇。这里只有一位医生——Miss Ada。她非常友善&#xff0c;没有人曾经对她说过坏话&#xff0c;所以谁能想到Ada会在她的房子里被发现死亡&#xff1f;世界著名侦探Gawry先生被任命查找罪犯。他询问Ada的邻居关于那个不幸的日子里拜访…

Java回收垃圾的基本过程与常用算法

目录 一、基本概述 二、垃圾分类 基本背景 举例说明各种引用类型的作用 强引用&#xff08;Strong Reference&#xff09; 软引用&#xff08;Soft Reference&#xff09; 弱引用&#xff08;Weak Reference&#xff09; 虚引用&#xff08;Phantom Reference&#xff…

广搜的优化技巧(备赛中)

A.电路维修 这道题我们对于每一个点都有四个方向&#xff0c;分别为 char op[]{"\\/\\/"}; 如果我们当前点到下一个点的方向不是对应的方向时我们的distance就加1&#xff0c;因为我们要求最优距离&#xff0c;所以我们采取一个小贪心的法则&#xff0c;每一次我们将…

「神州数码DCN」SAVI在IPV6环境下的应用

前言 介绍 ISIS&#xff0c;中间系统到中间系统的网络协议&#xff0c;最初是OSI组织为了他的CLNP&#xff08;类似于TCP/IP中的IP网络&#xff09;而设计的动态路由协议&#xff0c;后IETF对其进行修改和填充&#xff0c;现可以在TCP/IP和OSI环境中使用&#xff0c;称为&…

JavaWeb学习------jQuery

JavaWeb学习------jQuery jQuery函数库下载 jQuery函数库下载官网&#xff1a;Download jQuery | jQuery配套资料&#xff0c;免费下载 链接&#xff1a;https://pan.baidu.com/s/1aXBfItEYG4uM53u6PUEMTg 提取码&#xff1a;6c9i 然后下载&#xff1f; 来到官网&#xf…

Spark 1:Spark基础入门

Spark是什么 定义&#xff1a;Apache Spark是用于大规模数据&#xff08;large-scala data&#xff09;处理的统一&#xff08;unified&#xff09;分析引擎。 Spark 借鉴了 MapReduce 思想发展而来&#xff0c;保留了其分布式并行计算的优点并改进了其明显的缺陷。让中间数据存…

Winform从入门到精通(36)—ColorDialog(史上最全)

文章目录 前言一、属性1、AllowFullOpen2、AnyColor3、Color4、FullOpen5、ShowHelp6、SolidColorOnly7、Tag二、事件1、HelpRequest前言 当我们需要设置某个控件的颜色时,并且需要弹出一个可以选择颜色的对话框时,这时候就需要使用ColorDialog 一、属性 1、AllowFullOpen…

详解正则化

&#xff08;一&#xff09;正则化目的 防止过拟合现象&#xff0c;通过降低模型在训练集上的精度来提高其泛化能力&#xff0c;从而增加正则项 常见的降低过拟合方法 ■增加数据集的数据个数。数据量太小时&#xff0c;非常容易过拟合&#xff0c;因为 小数据集很容易精确拟…

Linux线程相关函数:线程的创建、回收、退出、取消

1. 线程号 进程号在系统中唯一&#xff0c;但线程号只在其所属进程环境中有效。 &#xff08;1&#xff09;pthread_self函数 #include<pthread.h>pthread_t pthread_self(void); /* 功能&#xff1a;获取线程号 返回值&#xff1a;调用此函数线程的ID */ pthread_se…

基于ssm的论坛系统的设计与实现【附源码】

基于ssm的论坛系统的设计与实现 摘 要 早期的网络论坛系统已经诞生一段时间&#xff0c;随着互联网技术的发展&#xff0c;它已经从最初的简单电子公告板系统变成了一种丰富的论坛系统社区模型。人们通过论坛系统进行信息的获取、发布和交流已经成为一种普遍的社交方式&#x…

一键开关机电路

一键开关机电路&#xff0c;通常用在防止关机导致数据保存发生错误&#xff0c;特别是在写EEPROM&#xff0c;FLASH和SD卡时&#xff0c;如果正在写入数据时断电&#xff0c;可能会导致数据保存错误&#xff0c;甚至导致元件损坏。一键开关机电路是由CPU来掌控&#xff0c;决定…

零基础带你认识HTML常用标签

目录 HTML 结构认识 HTML 标签HTML 文件基本结构标签层次结构快速生成代码框架 HTML 常见标签注释标签标题标签&#xff1a;h1 - h6水平线 hr 标签段落标签: p换行标签: br格式化标签图片标签: imgimg 标签的其他属性 超链接标签: a 表格标签基本使用和并单元格 列表标签表单标…