我们通常认为一个类有两种不同的用户:普通用户类的实现者。其中,普通用户编写的代码使用类的对象,这部分代码只能访问类的公有(接口)成员;实现者则负责编写类的成员和友元的代码,成员和友元既能访问类的公有部分,也能访问类的私有部分。如果进一步考虑继承的话就会出现第三种用户,即派生类。派生类可以访问基类的公有(public)成员和受保护(protected)成员,但不能访问基类的私有(private)成员。

继承相关点:

  • 大多数类都只继承自一个类,这种形式的继承叫做“单继承”。本文主要讲的是单继承。
  • 一个派生类的对象中,包含继承自基类的部分和派生类自定义的部分。正因为派生类含有基类部分,所以可以进行派生类到基类的类型转换,这种转换是隐式的。
  • 不存在从基类向派生类的隐式类型转换。
  • 派生类向基类的自动类型转换只对指针或引用有效,对象之间不存在类型转换。
  • 如果基类定义了静态成员,则不论派生出多少个派生类,每个静态成员都只存在唯一实例。
  • 防止一个类被继承可以使用关键字final,这时C++11新标准中提供的。
  • 继承中的虚函数与纯虚函数(见文章)。

一、公有、私有和受保护成员

1 . 访问说明符

在C++中通过使用访问说明符public、protected、private来对类的成员进行访问控制,控制成员对于普通用户或派生类来说是否可访问:

  • public:定义为public的成员对普通用户、类的实现者、派生类都是可访问的。public通常用于定义类的外部接口。

  • protected:定义protected成员的目的是让派生类可以访问而禁止其他用户访问。所以类的实现者和派生类可以访问,而普通用户不能访问。

  • private:定义为private的成员只能被类的实现者(成员和友元)访问。private部分通常用于封装(即隐藏)类的实现细节。

class People{
protected:
string name;
}; class Student : public People{
public:
friend void Print(Student &s);
friend void Print(People &p);
}; // 正确,可以通过派生类对象访问基类的protected成员
void Print(Student &s){ s.name="Songlee"; cout<< s.name << endl; }
// 错误,不能通过基类对象访问基类的protected成员
void Print(People &p){ p.name="Songlee"; cout<< p.name << endl; }

需要注意的是,派生类的成员或友元只能通过派生类对象来访问基类的受保护成员。派生类对于一个基类对象中的受保护成员没有任何访问特权。

2 . 改变成员的可访问性

有时我们需要改变派生类继承的某个名字的访问级别,通过使用using声明:

class People{
protected:
string name;
}; class Student : public People{
public:
using People::name; // 将继承来的name成员的访问权限改为public
}; int main()
{
Student me;
me.name = "SongLee"; // 可以访问name了
cout << me.name << endl;
return 0;
}

通过在类的内部使用using声明语句,我们可以将该类的直接或间接基类中的任何可访问成员(非私有成员)标记出来,改变其访问权限。

二、公有、私有和受保护继承

我们注意到,在类的派生列表中用到了访问说明符public、protected和private,它们分别表示不同的继承方式:

class A : public B { /*  */ };     // 公有继承
class A : private B { /* */ }; // 私有继承
class A : protected B { /* */ }; // 受保护继承

派生类的派生列表中的访问说明符对于派生类的成员(及友元)能否访问其直接基类的成员没什么影响。派生类的成员(及友元)对基类成员的访问权限只与基类中的访问说明符有关。

那么派生列表中的访问说明符有什么作用呢?

派生列表中访问说明符的作用控制派生类用户对于基类成员的访问权限,注意是派生类的用户。下面给出不同的继承方式导致的访问权限的变化:

  • public继承:如果继承是公有的,则成员将遵循其原有的访问说明符。父类中的public、protected和private属性在子类中不发生改变。

  • protected继承:比protected级别高的访问权限会变成protected。即父类中的public属性在子类中变为protected,父类中的protected和private属性在子类中不变。

  • private继承:比private级别高的访问权限会变成private。即父类中的三种访问属性在子类中都会变成private。

class A {   // 基类
public:
string A_public; // 公有成员
protected:
string A_protected; // 受保护成员
}; class B : private A { // 私有继承
public:
B(){ A_public="public"; A_protected="protected"; };
}; int main()
{
B b; // 通过B的对象访问
cout << b.A_public <<" "<< b.A_protected << endl;  // 错误,因为是私有继承
return 0;
}

如果我们在派生列表中不使用访问说明符,则struct关键字默认的是公有继承,class关键字默认的是私有继承。不过建议在继承时最好显式地将访问说明符写出来。

另外,不同的继承方式也会影响派生类向基类的转换,假定Derive继承自Base:

  • 只有当Derive公有地继承自Base时,用户代码才能使用派生类向基类的转换;如果Derive继承Base的方式是受保护的或者私有的,则用户代码不能使用该转换。

  • 不论Derive以什么方式继承Base,Derive的成员函数和友元都能使用派生类向基类的转换;派生类向其直接基类的类型转换对于派生类的成员和友元来说永远是可访问的。

  • 如果Derive继承Base的方式是公有的或者受保护的,则Derive的派生类的成员和友元可以使用Derive向Base的类型转换;反之,如果Derive继承Base的方式是私有的,则不能使用。

C++学习之继承中的访问控制的更多相关文章

  1. java学习笔记-继承中super关键字

    背景: 在java继承的概念中我们得知,被声明为私有的类成员对所属的类来说仍然是私有的.类之外的任何代码都不能访问,包括子类. super关键字的两种用法: 1.用于调用超类的构造函数: 2.用于访问 ...

  2. Java学习之继承中的执行顺序详解

    代码块(理解) (1)用{}括起来的代码. (2)分类: A:局部代码块 用于限定变量的生命周期,及早释放,提高内存利用率. B:构造代码块 把多个构造方法中相同的代码可以放到这里,每个构造方法执行前 ...

  3. C++学习笔记-继承中的构造与析构

    C++存在构造函数与析构函数,继承中也存在构造和析构函数.继承中的构造和析构函数与普通的构造析构有细微差别. 赋值兼容性原则 #include "iostream" using n ...

  4. C++学习之继承中的成员访问控制

    由基类到派生类的过程中,在派生类里如何访问基类成员,以及派生类对象如何访问基类成员,是根据派生类在从基类派生时是以什么方式进行的派生:public.protect或者private.下面说一下在这三种 ...

  5. C++中的类所占内存空间+继承中的成员访问控制

    C++学习之继承中的成员访问控制 C++中的类所占内存空间总结

  6. JavaScript学习13 JavaScript中的继承

    JavaScript学习13 JavaScript中的继承 继承第一种方式:对象冒充 <script type="text/javascript"> //继承第一种方式 ...

  7. Java学习笔记十九:Java中的访问控制修饰符

    Java中的访问控制修饰符 一:Java修饰符的种类: 访问修饰符 非访问修饰符 修饰符用来定义类.方法或者变量,通常放在语句的最前端.我们通过下面的例子来说明: public class Hello ...

  8. (C/C++学习)5.C++中的虚继承-虚函数-多态解析

    说明:在C++学习的过程中,虚继承-虚函数经常是初学者容易产生误解的两个概念,它们与C++中多态形成的关系,也是很多初学者经常产生困惑的地方,这篇文章将依次分别对三者进行解析,并讲述其之间的联系与不同 ...

  9. Android(java)学习笔记119:继承中父类没有无参构造

    /* 如果父类没有无参构造方法,那么子类的构造方法会出现什么现象呢? 报错. 如何解决呢? A:在父类中加一个无参构造方法 B:通过使用super关键字去显示的调用父类的带参构造方法 C:子类通过th ...

随机推荐

  1. git reset作用

    git reset: 1. 文件从暂存区回退到工作区,撤销add 2. 版本回退  一:文件从暂存区回退到工作区,撤销add 如果想取消某个add的文件,可以使用该命令来进行撤销操作 撤消add:gi ...

  2. 读《An Adaptable and Extensible Geometry Kernel》

    读<An Adaptable and Extensible Geometry Kernel> 利用Curiously Recurring Template Pattern替代虚函数 详细内 ...

  3. MAGENTO 插件

    导航放到右侧:magento-community/RicoNeitzel_VertNav 后台图片管理显示图片:magento-community/TBT_Enhancedgrid magento-c ...

  4. HDU_2955_Robberies_01背包

    A - Robberies Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submi ...

  5. swift Enumerations

    swift Enumerations enum.case.switch CaseIterable allCases 要区别枚举变量和关联值 枚举变量参与枚举运算: 关联值和rawvalue不参与. A ...

  6. 安卓app测试之Monkeyscript

    MonkeyScript是一组可以被Monkey识别的命令集合 优点:MonkeyScript可以完成重复固定的操作 使用:adb shell monkey -f <scriptfile> ...

  7. Jmeter在windows上安装和环境配置

    一.Jmeter简单介绍 Apache JMeter是Apache组织开发的基于Java的压力测试工具.它可以用于测试静态和动态资源,例如静态文件.Java小服务程序.CGI 脚本.Java 对象.数 ...

  8. ES 提案的各状态

    JavaScrpit,亦即 ECMAScript,新功能的演进是由一个叫 TC39 这么个组织在统筹协调和推进的. 一般新特性会由社区先提案,被采纳后开始进入下一流程.一个提案到最终落地到成为标准,需 ...

  9. Ansible基于playbook批量修改主机名实战

    Ansible基于playbook批量修改主机名 安装Ansible,相信这里也不用多说,大家都知道 说一下环境:这里的主机名是修改之后的,我先把其他两台的主机名改为别的 192.168.30.21 ...

  10. java几种连接数据库的方法

    package bean; import java.sql.Connection;import java.sql.DriverManager; public class jdbcTest { //不同 ...