author:&Carlton
tag:C++
topic:【C++】友元,向前声明,黑马程序员案例,解决错误:错误C2653:不是类或命名空间,错误C2027:使用了未定义类型
website:黑马程序员C++
date:2023年7月20日
目录
友元
在A类中声明B类为友元,则B可以访问A类中的私有属性。
这里涉及了类与类之间的相互访问,从编译器的视角来看,很容易带来“我认不清人”的问题,因为编译器的编译顺序是固定的,没有那么智能,所以我们要告诉它,使用向前声明。
因为编译器的"迟钝",编译常见的报错有:
①不是类或命名空间
②使用了未定义类型
但原因是类似的,可归为上述抽象的“认不得”,解决方法可以使用向前声明,让它认得。
向前声明
类A要通过成员函数访问类B,则先声明类B(让编译器在看到类A的声明时初步认知类B),然后声明类A(让编译器知道类A大概有些啥东西,这里头有对类B的引用,此时编译器已经初步认知过不完全类型B),再定义类B(编译器完全了解类B了,知道了类B的属性,发现了类A是类B的友元,此时编译器已经初步认识不完全类型A了),最后定义类A(编译器完全了解类A了,主要去知道了类A如何通过成员函数访问类B的某个属性了)
为何忽然变得这么麻烦呢?(和以往我们不做友元的时候相比)
因为我们要在类B的定义里对类A进行友元声明。
不完全类型
不完全类型只能以有限方式使用,不能定义该类型的对象,不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。
源代码及相关解释
关键点1:
向前声明类Building,使得类ReneeHappy的向前声明中“Building* m_building”有效,此时Building还是一个不完全类型,不能使用它的成员属性或者成员函数。
在使用向前声明后,类定义前,只能定义指向该不完全类型building的指针及引用而不能使用该类成员(正如在类ReneeHappy中的"Builiding* m_building")。
但与Building类属性有关的ReneeHappy成员函数visit的定义就需要放在类Building定义的后头了,因为需要让编译器先知道类Building到底有什么成员才能使用其成员
关键点2:
为何类Renee不需要像类ReneeHappy那样进行向前声明就可以访问类Building的成员呢?
是因为在类Building的定义中对类Renee是整体声明,不需要了解它有什么成员,而对类ReneeHappy是需要知道它有成员函数visit的。
请看!源代码:
#include <iostream>
#include <string>
using namespace std;
class Building;
/*
关键点1:
向前声明类Building,使得类ReneeHappy的向前声明中“Building* m_building”有效
此时Building还是一个不完全类型,不能使用它的成员属性或者成员函数。
在使用向前声明后,类定义前,只能定义指向该不完全类型building的指针及引用而不能使用该类成员(正如在类ReneeHappy中的"Builiding* m_building")
但与Building类属性有关的ReneeHappy成员函数visit的定义就需要放在类Building定义的后头了,因为需要让编译器先知道类Building到底有什么成员才能使用其成员
*/
//tips:不完全类型只能以有限方式使用,不能定义该类型的对象,不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数
//类ReneeHappy的声明
class ReneeHappy
{
public:
Building* m_building;
//构建ReneeHappy时让指向Building类的指针类型building指向堆中的内存区
ReneeHappy();
void visit();
};
/*
向前声明类ReneeHappy,使得编译器在后头类Building的定义中认识ReneeHappy是一个不完全类型,可以对其进行友元声明操作
*/
class Building
{
//声明全局函数是Building类的友元
friend void ReneeVisit();
//声明类Renee是Building的友元
friend class Renee;
//声明类ReneeHappy的成员函数visit是Building的友元
friend void ReneeHappy::visit();
public:
Building();//声明一下构建函数
string m_SittingRoom;
private:
string m_BedRoom;
};
//在类外定义Building的构建函数并使用初始化列表
Building::Building(): m_SittingRoom("Carlton's 客厅"),m_BedRoom("Carlton's 卧室")
{
cout << "Building的构建函数" << endl;
}
//全局函数ReneeVisit的定义
void ReneeVisit()
{
Building building;
cout << "全局函数ReneeVisit正在访问:" << building.m_SittingRoom << endl;
cout << "全局函数ReneeVisit正在访问:" << building.m_BedRoom << endl;
}
//类Renee的定义
class Renee
{
public:
Building* m_building;
//构建Renee时让指向Building类的指针类型building指向堆中的内存区
Renee();
void visit()
{
cout << "类Renee正在访问: " << m_building->m_SittingRoom << endl;
cout << "类Renee正在访问: " << m_building->m_BedRoom << endl;
}
};
//类Renee的构建函数写在外头
Renee::Renee()
{
m_building = new Building;
}
/*
关键点2:
为何类Renee不需要像类ReneeHappy那样进行向前声明就可以访问类Building的成员呢?
是因为在类Building的定义中对类Renee是整体声明,不需要了解它有什么成员,而对类ReneeHappy是需要知道它有成员函数visit的
*/
//类ReneeHappy的构建函数和成员函数visit写在类外头
ReneeHappy::ReneeHappy()
{
m_building = new Building;
}
void ReneeHappy::visit()
{
cout << "类ReneeHappy的成员函数visit正在访问: " << m_building->m_SittingRoom << endl;
cout << "类ReneeHappy的成员函数visit正在访问: " << m_building->m_BedRoom << endl;
}
//全局函数做友元
void test01()
{
ReneeVisit();
}
//类做友元
void test02()
{
class Renee p;
p.visit();
}
//成员函数做友元
void test03()
{
class ReneeHappy p;
p.visit();
}
int main()
{
test01();
test02();
test03();
return 0;
}
参考
第一个链接的博主更书面地介绍了向前声明、另外分享了单独编译的解决办法
第二个和第三个链接的博主对解决问题的过程做了更具体、详细的记录。
谢谢你们!真的很用心🤠
http://t.csdn.cn/EafaC 纯白棒球帽
欢迎指正与分享,谢谢!