0

0

C++组合类型中访问修饰符使用方法

P粉602998670

P粉602998670

发布时间:2025-09-12 10:48:01

|

692人浏览过

|

来源于php中文网

原创

在C++组合类型中,访问修饰符控制类成员的可见性,private成员仅类内可访问,public成员可被外部访问,protected成员供派生类使用;组合关系下,外层类只能通过内层对象的public接口与其交互,即使内层成员为protected,外层类也无法访问,因protected仅在继承体系中生效,组合不构成继承关系,故外层类与被组合对象间仍需遵循封装原则,通过public方法间接操作,确保安全性与低耦合。

c++组合类型中访问修饰符使用方法

在C++的组合类型中,访问修饰符(

public
private
protected
)扮演着核心角色,它们决定了类成员的可见性和可访问性。简单来说,它们定义了“谁能看到什么”和“谁能操作什么”。当一个类包含另一个类的对象时(即组合关系),这些修饰符就决定了外部代码,以及包含这个对象的“外层”类,如何与被包含的“内层”对象的成员进行交互。理解这一点,对于构建健壮、可维护的C++系统至关重要。

解决方案

C++的访问修饰符,

public
private
protected
,在组合类型中的使用方法,其实是其基本规则的延伸,但常常会带来一些独特的思考点。

首先,我们得明确这三个修饰符的基本含义:

  • public
    : 任何外部代码都可以直接访问。
  • private
    : 只有类内部的成员函数和友元可以访问。
  • protected
    : 只有类内部的成员函数、友元以及其派生类的成员函数可以访问。

在组合关系中,一个类(我们称之为“容器类”或“外层类”)包含另一个类的对象(我们称之为“被组合对象”或“内层对象”)作为其成员。此时,访问修饰符的作用体现在两个层面:

立即学习C++免费学习笔记(深入)”;

  1. 被组合对象本身的访问修饰符: 这决定了该对象自身的成员(数据和函数)对外界的可见性。
  2. 被组合对象在容器类中的访问修饰符: 这决定了容器类如何访问其内部的被组合对象。

举个例子,假设我们有一个

Engine
类和一个
Car
类。
Car
类“拥有”一个
Engine
对象。

// Engine.h
class Engine {
private:
    int horsepower;
    void igniteSparkPlugs() { /* ... */ }

public:
    Engine(int hp) : horsepower(hp) {}
    void start() {
        igniteSparkPlugs(); // Engine内部可以访问private成员
        // ...
    }
    int getHorsepower() const { return horsepower; }
};

// Car.h
class Car {
private:
    Engine engine; // Engine对象作为Car的private成员
    // ...

public:
    Car(int engineHP) : engine(engineHP) {}

    void drive() {
        // Car类可以访问其private成员engine
        engine.start(); // Car可以调用Engine的public方法
        // engine.igniteSparkPlugs(); // 错误:Car无法访问Engine的private方法
        // ...
    }

    int getEnginePower() const {
        return engine.getHorsepower(); // Car可以调用Engine的public方法
    }
};

// main.cpp
int main() {
    Car myCar(200);
    myCar.drive();
    // myCar.engine.start(); // 错误:main函数无法直接访问Car的private成员engine
    // myCar.engine.getHorsepower(); // 错误:同上
    return 0;
}

从这个例子可以看出:

  • Engine
    类的
    private
    成员(
    horsepower
    ,
    igniteSparkPlugs
    )只能由
    Engine
    类自己的方法访问。
  • Engine
    类的
    public
    成员(
    start
    ,
    getHorsepower
    )可以被任何拥有
    Engine
    对象访问权限的代码调用。
  • Car
    类将
    Engine
    对象声明为
    private
    成员。这意味着只有
    Car
    类自己的方法可以访问
    Engine
    对象。外部代码(如
    main
    函数)无法直接通过
    myCar.engine
    来访问它。
  • Car
    类的方法(如
    drive
    getEnginePower
    )访问
    Engine
    对象时,它只能调用
    Engine
    对象的
    public
    方法。即使
    Car
    拥有
    Engine
    ,它也无法“穿透”
    Engine
    自身的封装,去访问
    Engine
    private
    成员。

说白了,访问修饰符是作用在“点”运算符(

.
)之前的。
myCar.engine
中的
Engine
Car
的成员,其访问权限由
Car
类内部决定。而
engine.start()
中的
start
Engine
的成员,其访问权限由
Engine
类内部决定。这两个层面的权限是独立判断的。

如何在C++组合设计中,有效地利用访问修饰符来提升代码的封装性和安全性?

在C++的组合设计中,巧妙运用访问修饰符是构建高内聚、低耦合系统的关键。我个人认为,其核心在于“默认私有,按需开放”的原则,以及对“接口与实现分离”的深刻理解。

首先,将组合成员设为

private
是常态。就像上面的
Car
Engine
例子,
Car
内部的
Engine
对象通常不应该直接暴露给外部。为什么?因为
Engine
Car
的内部实现细节。如果外部代码可以直接操作
Car
Engine
,那么
Car
的内部状态就可能被随意修改,导致
Car
的行为变得不可预测。这破坏了
Car
的封装性,使得
Car
类在未来的维护和修改中变得异常脆弱。想象一下,如果
Car
Engine
public
的,用户可以直接
myCar.engine.igniteSparkPlugs()
,这显然不是我们希望的。

仿QQ官方商城整站 for ECSHOP
仿QQ官方商城整站 for ECSHOP

ECSHOP仿QQ官方商城整站源码,基于ECSHOP V2.7.3制作。整体采用黑色。费用漂亮。适合综合,包包,首饰类商城网站使用。 安装方法:1.访问:域名/install,按照程序提示进行安装。2.登陆网站后台,然后进行数据还原。3.模板设置中,选择QQSHOW模板4.清空缓存。。。 注:还原数据后,网站后台信息:后台地址:admin后台用户名:admin后台密码:www.shopex5.co

下载

其次,通过容器类的

public
接口来间接操作被组合对象。当外部代码需要与被组合对象交互时,它应该通过容器类提供的
public
方法来实现。这些
public
方法充当了“守门员”的角色,它们可以进行必要的校验、转换或协调,确保操作的合法性和一致性。例如,
Car
提供了
drive()
方法,这个方法内部会调用
engine.start()
。这样,外部只需要知道如何“驾驶”汽车,而无需关心引擎是如何启动的。这提升了抽象层次,让用户更容易使用,也让
Car
类有更大的自由度去改变其内部引擎的实现,而不会影响到外部代码。

再者,警惕

friend
关键字的使用。虽然
friend
可以突破封装,允许一个类访问另一个类的
private
protected
成员,但在组合关系中,这通常是一个“红色警报”。如果发现自己频繁地需要使用
friend
来让容器类访问被组合对象的私有成员,那往往意味着设计上可能存在问题,比如职责划分不清,或者被组合对象没有提供足够友好的
public
接口。
friend
应当被视为一种非常规手段,只在极少数、经过深思熟虑且有充分理由的情况下使用,例如在实现某些特定的设计模式(如迭代器)时。过度使用
friend
会显著降低代码的可维护性和安全性。

最后,利用

const
正确表达意图。如果一个容器类的方法只是需要读取被组合对象的状态,而不应该修改它,那么将相应的访问器方法声明为
const
,并确保通过
const
引用或
const
指针返回被组合对象(如果需要返回的话),可以进一步提升安全性。这能让编译器帮助我们强制执行只读的语义,避免意外的副作用。

C++组合类型中,访问修饰符与继承关系下的
protected
有何不同考量?

这是一个非常好的问题,因为

protected
在继承和组合这两种关系中的表现和意图确实有着显著的区别,常常是C++初学者感到困惑的地方。

在继承关系中,

protected
的意义是“对子类开放,对外界隐藏”。当一个基类成员被声明为
protected
时,它意味着这个成员可以被基类自身以及所有直接或间接派生自这个基类的子类访问。但对于基类及其派生类之外的任何其他代码,这个
protected
成员是不可见的,就像
private
成员一样。
protected
在这里的目的是为了方便子类实现其特有的行为,同时又避免将基类的内部细节完全暴露给外部,从而保持一定的封装性。它建立了一种“家族特权”的概念。

class Base {
protected:
    int baseData;
    void baseMethod() { /* ... */ }
public:
    Base() : baseData(10) {}
};

class Derived : public Base {
public:
    void accessBase() {
        baseData = 20; // OK: Derived可以访问Base的protected成员
        baseMethod();  // OK: Derived可以访问Base的protected方法
    }
};

// main.cpp
int main() {
    Derived d;
    // d.baseData = 30; // 错误:main函数不能访问protected成员
    return 0;
}

然而,在组合关系中,

protected
的考量则完全不同。 当一个类
A
包含一个类
B
的对象作为其成员时,
A
B
之间并没有继承关系。这意味着,即使
B
的某个成员被声明为
protected
,类
A
无法直接访问它。对于类
A
而言,类
B
protected
成员和
private
成员是等价的——它们都只能由类
B
自身或其派生类访问。类
A
只能通过类
B
public
接口来与
B
的对象进行交互。

我们回到

Car
Engine
的例子。如果
Engine
有一个
protected
成员:

class Engine {
protected:
    int engineID; // 假设这是Engine的protected成员
    void internalCheck() { /* ... */ }

private:
    int horsepower;
public:
    Engine(int hp) : horsepower(hp), engineID(100) {}
    void start() { internalCheck(); }
    int getHorsepower() const { return horsepower; }
};

class Car {
private:
    Engine engine;

public:
    Car(int engineHP) : engine(engineHP) {}

    void inspectEngine() {
        // engine.engineID = 200; // 错误:Car无法访问Engine的protected成员
        // engine.internalCheck(); // 错误:Car无法访问Engine的protected方法
        engine.start(); // OK: 通过public接口间接调用internalCheck
    }
};

从代码中可以清晰地看到,

Car
类对
Engine
protected
成员
engineID
internalCheck()
是无权访问的。
Car
类与
Engine
类之间是一种“拥有”关系,而不是“是”的关系(继承)。
protected
的“家族特权”只适用于继承体系内部,它不会因为一个类包含了另一个类的对象而传递。

因此,在组合设计中,如果被组合对象的某个成员被声明为

protected
,那通常是为该被组合对象自身的继承体系所准备的。对于包含它的容器类来说,这个
protected
成员与
private
成员在使用上并没有区别,都是不可直接访问的内部实现细节。这种清晰的界限有助于维护组合关系的独立性和封装性,避免了因“拥有”关系而产生的意外耦合。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1435

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

224

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

85

2025.10.17

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

519

2023.09.20

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

989

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

50

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

222

2025.12.29

什么是低代码
什么是低代码

低代码是一种软件开发方法,使用预构建的组件可快速构建应用程序,无需大量编程。想了解更多低代码的相关内容,可以阅读本专题下面的文章。

277

2024.05.21

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 5.7万人学习

C 教程
C 教程

共75课时 | 3.8万人学习

C++教程
C++教程

共115课时 | 10.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号