[C++游戏开发基础:嵌套类型]

考虑下面程序:

#include <iostream>

enum class FruitType
{
	apple,
	banana,
	cherry
};

class Fruit
{
private:
	FruitType m_type { };
	int m_percentageEaten { 0 };

public:
	Fruit(FruitType type) :
		m_type { type }
	{
	}

	FruitType getType() { return m_type; }
	int getPercentageEaten() { return m_percentageEaten; }

	bool isCherry() { return m_type == FruitType::cherry; }

};

int main()
{
	Fruit apple { FruitType::apple };

	if (apple.getType() == FruitType::apple)
		std::cout << "I am an apple";
	else
		std::cout << "I am not an apple";

	return 0;
}

目前为止,我们已经介绍过具有两种不同类型成员的类类型:

  • 数据成员
  • 成员函数

类类型还支持另一种成员,那就是 嵌套类型(也称为成员类型)。创建嵌套类型很简单,只需要在类内部选择一个合适的访问修饰符下进行类型定义即可。下面的例子在功能上和上面的是一样的,但是它采用了嵌套类型的定义方式:

class Fruit
{
public:
	// FruitType 已经被移动到类内部,并放置在 public 访问控制符下
  // 我们还将其重命名为 Type,并将其更改为普通的枚举(enum),而不是 enum class
	enum Type
	{
		apple,
		banana,
		cherry
	};

private:
	Type m_type {};   // 存储水果类型的成员变量
	int m_percentageEaten { 0 }; // 记录被吃掉的百分比

public:
	Fruit(Type type) :
		m_type { type }
	{
	}

	Type getType() { return m_type;  }  // 获取水果类型
	int getPercentageEaten() { return m_percentageEaten;  }  // 获取被吃掉的百分比

	// 在 Fruit 的成员函数内部,我们不再需要使用 Fruit::Type:: 前缀
	bool isCherry() { return m_type == cherry; }
};

int main()
{
	// 注意:在类的外部,我们仍然需要使用 Fruit:: 前缀来访问枚举值
	Fruit apple { Fruit::apple };

	if (apple.getType() == Fruit::apple)
		std::cout << "I am an apple";
	else
		std::cout << "I am not an apple";

	return 0;
}
  • 嵌套类型 Type 已在类的顶部定义。嵌套类型名称必须在使用之前完全定义,因此通常先定义它们。
  • 嵌套类型遵循正常的访问规则。Type是在public修饰符下定义的,这样类型名称和枚举就可以被公开访问。
  • 类类型充当在其中声明的名称的作用域,就像命名空间一样。因此,Type的全限定名是Fruit::Type,apple的完全限定名是Fruit::apple
  • 在类的成员中,我们不需要使用完全限定名。例如,在成员函数 isCherry() 中,我们访问 cherry 枚举器而不使用 Fruit::cherry限定符。
  • 但在类外部,我们必须使用完全限定名(例如 Fruit::apple)。我们将 FruitType 重命名为 Type,这样我们就可以以 Fruit::Type 的形式访问它(而不是更冗余的 Fruit::FruitType)。

类类型也可以包含嵌套的typedef或类型别名:

#include <iostream>
#include <string>
#include <string_view>

class Employee
{
public:
    using IDType = int; // 定义 IDType 类型别名

private:
    std::string m_name{}; // 员工姓名
    IDType m_id{};        // 员工 ID
    double m_wage{};      // 员工工资

public:
    // 构造函数,初始化员工的姓名、ID 和工资
    Employee(std::string_view name, IDType id, double wage)
        : m_name { name }
        , m_id { id }
        , m_wage { wage }
    {
    }

    const std::string& getName() { return m_name; } // 获取员工姓名
    IDType getId() { return m_id; } // 在类内部可以直接使用 IDType,而不需要限定作用域
};

int main()
{
    Employee john { "John", 1, 45000 }; // 创建一个 Employee 对象
    Employee::IDType id { john.getId() }; // 在类外部,必须使用完全限定名称 Employee::IDType

    std::cout << john.getName() << " has id: " << id << '\n';

    return 0;
}

C++标准库中的类使用嵌套类型定义是非常常见的。


嵌套类和外部类的访问关系

类将其他类作为嵌套类型是相当罕见的,但这不代表不可能(Java过来的兄弟就知道内部类在Java中的位置)。

在 C++中,嵌套类不能访问外部(包含)类的 this 指针,因此嵌套类不能直接访问外部类的成员。这是因为嵌套类可以独立于外部类进行实例化(在这种情况下,将没有外部类成员可供访问!)

但是由于嵌套内是外部类的成员,因此他们可以访问范围内内部类的任何私有成员。

class Employee
{
public:
    using IDType = int;

    class Printer
    {
    public:
        void print(const Employee& e) const
        {
            std::cout << e.m_name << " has id: " << e.m_id << '\n';
        }
    };

private:
    std::string m_name{};
    IDType m_id{};
    double m_wage{};

public:
    Employee(std::string_view name, IDType id, double wage)
        : m_name{ name }
        , m_id{ id }
        , m_wage{ wage }
    {
    }

    // 在本示例中移除了访问函数(因为它们未被使用)。
};

int main()
{
    const Employee john{ "John", 1, 45000 };
    const Employee::Printer p{}; // 实例化内部类 Printer 的对象
    p.print(john);

    return 0;
}
  • Printer 无法访问 Employee 的 this 指针,
  • 因此我们无法直接打印 m_name 和 m_id。
  • 相反,我们必须传递一个 Employee 对象来使用。
  • 由于 Printer 是 Employee 的成员类,
  • 我们可以直接访问私有成员 e.m_name 和 e.m_id。

在标准库中,嵌套类的使用十分频繁,大多数的迭代器类都是使用这种方式实现的。


嵌套类&前向声明

嵌套类可以在封装它的外部类中进行前向声明。然后可以在外部类内部或者外部第一嵌套类型 :

#include <iostream>

class outer
{
public:
    class inner1;   // ✅ 允许:在外部类(outer)内部进行前向声明  
    class inner1{}; // ✅ 允许:在外部类(outer)内部定义之前前向声明的类型  
    class inner2;   // ✅ 允许:在外部类(outer)内部进行前向声明  
};

// ✅ 允许:在外部类(outer)外部定义之前前向声明的 inner2
class outer::inner2 
{
};

int main()
{
    return 0;
}

但是,嵌套类型不能在定义外部类之前进行前向声明:

#include <iostream>

class outer;         // ✅ 允许:可以对非嵌套类型(外部类)进行前向声明
class outer::inner1; // ❌ 错误:在定义 outer 之前,不能前向声明它的嵌套类型 inner1

class outer
{
public:
    class inner1{}; // ℹ️ 注意:嵌套类型 inner1 在这里被声明和定义
};

// ✅ 允许(但多余):由于 inner1 已经作为 outer 类的一部分被声明,这里再次前向声明是多余的
class outer::inner1; 

int main()
{
    return 0;
}

说白了就是 必须先定义外部类(封闭类),然后才能声明或定义它的嵌套类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xayla

轻轻一点,暖心房

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值