您现在的位置是:首页 >学无止境 >C++前置声明的理解网站首页学无止境

C++前置声明的理解

无畏烧风 2023-05-02 22:30:02
简介C++前置声明的理解

知识补充

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

#ifndef MyWidget_H_
#define MyWidget_H_

#endif

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

在CC++中对于类或者结构体的大小总是确定了的,如果类或者结构体的大小无法确定那么编译就无法通过。如
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

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。