作用域与重定义(同名隐藏)

一样的,先上代码

 1 class A
2 {
3 public:
4 int a_data;
5 void a()
6 {
7 cout << "A" << endl;
8 }
9 };
10 class B
11 {
12 public:
13 int b_data;
14 void b()
15 {
16 cout << "B" << endl;
17 }
18 };
19 class C :public A, public B
20 {
21 public:
22 int c_data;
23 void a(int data)//重载A类中的a()函数
24 {
25 cout << "C" << endl;
26 }
27 };
28 int main()
29 {
30 C c;
31 c.a();
32 return 0;
33 }

编译后,编译器会报错错误表明:编译器并没有将c.a()看做C类继承自A类的a()函数,而是报错没有给a函数参数,即不构成函数重载,如果给c.a(10)一个参数,编译通过。输出:C

那么我们不给C类中定义同名函数呢

 1 class A
2 {
3 public:
4 int a_data;
5 void a()
6 {
7 cout << "A" << endl;
8 }
9 };
10 class B
11 {
12 public:
13 int _data;
14 void b()
15 {
16 cout << "B" << endl;
17 }
18 };
19 class C :public A, public B
20 {
21 public:
22 int _data;
23 //void a(int data)
24 //{
25 // cout << "C" << endl;
26 //}
27 };
28 int main()
29 {
30 C c;
31 c.a();
32 return 0;
33 }

编译通过,运行输出:A

以上两个例子,完全可以说明,当我们在派生类中定义一个同名函数的时候,编译器是将同名函数隐藏了,不管参数表是否相同。即不会构成函数重载,直接为函数覆盖。 
那么问题来了,为什么不会构成函数重载呢? 
一定要注意,函数重载的条件是在同一个作用域中才会构成函数重载,而派生类和基类是两个类域,一定不会构成函数重载的。

当然,我们还有另外一个方法访问同名时基类中的成员即

 1 int main()
2 {
3 C c;
4 c._data;
5 c.B::_data;
6 c.a();
7 c.A::a();
8 getchar();
9 return 0;
10 }

B类的成员变量_data与C类中的成员变量_data也构成同名隐藏。

1. 在继承体系中基类和派生类是两个不同作用域。

2. 子类和父类中有同名 成员 , 子类成员 将屏蔽父类对成员 的直接访问。 ( 在子类成员 函数中, 可以 使用 基类: : 基类成员 访问) --隐藏 --重定义

3. 注意在实际中在继承体系里面最好不要定义同名 的成员 。

继承与转换——赋值兼容规则(public继承为例)

派生类和基类之间的特殊关系为:

1.派生类对象可以赋值给基类对象 ;基类对象不能赋值给派生类。
2.基类指针可以在不进行显示类型转换的情况下指向派生类对象 。
3.基类引用可以再不进行显示类型转换的情况下引用派生类对象,但是基类指针或引用只能用于调用基类的方法,不能用基类指针或引用调用派生类的成员及方法。

 1 void FunTest(const Base&d)
2 {}
3 void FunTest1(const Derive&d)
4 {}
5 int main()
6 {
7 Derive d;
8 Base b(0);
9 b = d;//可以
10 d = b;//不行,访问的时候会越界
11 //上面已经解释过了
12 FunTest(b);
13 FunTest(d);
14 FunTest1(b); //报错
15 FunTest1(d);
16 Base* pBase = &d; //父类指针/或引用可以直接指向子类对象,但只能访问子类中继承父类的部分
17 Derive* pD = &b;//报错 子类的指针/引用不能直接指向父类对象
18 //如果非要这么做只能通过强制类型转换
19 Derive* pD = (Derive*)&b;//如果访问越界,程序会崩溃
20 }

通常,C++要求引用和指针类型与赋给的类型匹配,但这一规则对继承来说是个例外。但是这个例外是单向的,即仅仅不可以将基类对象和地址赋给派生类引用和指针。如果允许基类引用隐式的引用派生类对象,则可以使用基类引用为派生类对象调用基类的方法,因为派生类继承了基类的方法,所以这样不会出现问题。但是如果可以将基类对象赋给派生类引用,那么派生类引用能够为积累对象调用派生类方法,这样做会出现问题,例如:用基类对象调用派生类中新增的方法,是没有意义的,因为基类对象中根本没有派生类的新增方法。

友元与继承

 1 class Person
2 {
3 friend void Display(Person &p, Student&s);
4 protected:
5 string _name; // 姓名
6 };
7 class Student : public Person
8 {
9 protected :
10 int _stuNum; // 学号
11 };
12 void Display(Person &p, Student &s)
13 {
14 cout << p._name << endl;
15 cout << s._name << endl;
16 cout << s._stuNum << endl;
17 }
18 void TestPerson1()
19 {
20 Person p;
21 Student s;
22 Display(p, s);
23 }

友元关系不能继承, 也就是说基类友元不能访问子类私有和保护成员 。友元只是能访问指定类的私有和保护成员的自定义函数,不是被指定类的成员,自然不能继承。

使用友元类时应该注意:

(1) 友元关系不能被继承。 
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。

(3)友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

(4)友元可以访问类的私有成员。 
(5)友元只能出现在类定义内部,友元声明可以在类中的任何地方,一般放在类定义的开始或结尾。 
(6)友元可以是普通的非成员函数,或前面定义的其他类的成员函数,或整个类。 
(7)类必须将重载函数集中每一个希望设为友元的函数都声明为友元。 
(8)友元关系不能继承,基类的友元对派生类的成员没有特殊的访问权限。如果基类被授予友元关系,则只有基类具有特殊的访问权限。该基类的派生类不能访问授予友元关系的类。
继承与静态成员

了解这个问题之前,我们先确定一件事情,那就是,在基类和派生类中,静态成员和静态成员函数共用同一段空间。 我们在以前的学习过程中都知道,类的模型是所有对象的数据成员单独存储,但是所有成员函数和静态成员是共用一段空间的

 1 class Person
2 {
3 public :
4 Person() {++ _count; }
5 protected :
6 string _name ; // 姓名
7 public :
8 static int _count; // 统计人的个数。
9 } ;
10 int Person: : _count = 0;
11 class Student : public Person
12 {
13 protected :
14 int _stuNum ; // 学号
15 } ;
16 class Graduate : public Student
17 {
18 protected:
19 string _seminarCourse; // 研究科目
20 } ;
21 void TestPerson1()
22 {
23 Student s1;
24 Student s2;
25 Student s3;
26 Graduate s4;
27 cout<<"人数: "<<Person: : _count<<endl;
28 Student : : _count = 0;
29 cout<<"人数: "<<Person: : _count<<endl;
30 }

基类定义了 static成员 , 则整个继承体系里面只 有一个这样的成员 。 无论派生出多少个子类, 都只有 一个static成员 实例。父类的static变量和函数在派生类中依然可用,但是受访问性控制(比如,父类的private域中的就不可访问)。而且对static变量来说,派生类和父类中的static变量是共用空间的,这点在利用static变量进行引用计数的时候要特别注意。派生类的friend函数可以访问派生类本身的一切变量,包括从父类继承下来的protected域中的变量。但是对父类来说,他并不是friend的。

C++中的继承(3)作用域与重定义,赋值兼容规则的更多相关文章

  1. C++中的继承(3)作用域与重定义,赋值兼容规则

    1.作用域与重定义(同名隐藏) 一样的,先上代码 1 class A 2 { 3 public: 4 int a_data; 5 void a() 6 { 7 cout << " ...

  2. C++中的继承详解(3)作用域与重定义,赋值兼容规则

    作用域与同名隐藏 一样的,先上代码 1 class A 2 { 3 public: 4 int a_data; 5 void a() 6 { 7 cout << "A" ...

  3. C++语言笔记系列之十六——赋值兼容规则&amp;多继承的二义性

    1.赋值兼容规则 (1)派生类对象能够给基类对象赋值,这样的情况下派生类对象将从基类继承的成员的值赋值给一个基类对象:可是不同意将一个基类的对象赋值给一个派生类. (2)能够将派生类对象的地址赋给基类 ...

  4. c++ 公有继承的赋值兼容规则

    赋值兼容规则是指在需要基类对象的任何地方都可以使用公有派生类的对象来替代.通过公有继承,派生类得到了基类中除构造函数.析构函数之外的所有成员,而且所有成员的访问控制属性也和基类完全相同.这样,公有派生 ...

  5. C++学习笔记 封装 继承 多态 重写 重载 重定义

    C++ 三大特性 封装,继承,多态 封装 定义:封装就是将抽象得到的数据和行为相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成类,其中数据和函数都是类的成员,目的在于将对 ...

  6. VC中链接错误,提示string重定义

    VC链接错误,说是string已经有了实现了,只要 rebuild 一下好了. Linking...LINK : warning LNK4075: ignoring '/EDITANDCONTINUE ...

  7. 初步学习C++中的继承关系

    继承机制是面向对象程序设计使代码能够复用的最重要的手段,它同意程序猿在保持原有类特性的基础上进行扩展,添加功能. 这样产生新的类,称派生类.继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂 ...

  8. python的父类和子类中关于继承的不同版本的写法

    Python 2.7中的继承 在Python 2.7中,继承语法稍有不同,ElectricCar 类的定义类似于下面这样: class Car(object): def __init__(self, ...

  9. oracle在线重定义表

    在一个高可用系统中,如果需要改变一个表的定义是一件比较棘手的问题,尤其是对于7×24系统.Oracle提供的基本语法基本可以满足一般性修改,但是对于把普通堆表改为分区表,把索引组织表修改为堆表等操作就 ...

随机推荐

  1. Linux下进行硬盘挂载、分区、删除分区,格式化,卸载方法

    本文简单介绍了下文件系统及其操作(df命令),磁盘分区.格式化,还有最主要是挂载操作. 在这里对"挂载"做个说明,我们都知道文件系统是创建在磁盘上面的,每个文件系统都有独立的ino ...

  2. 基于Json序列化和反序列化通用的封装

    1. 最近项目已经上线了 ,闲暇了几天 想将JSON的序列化以及反序列化进行重新的封装一下本人定义为JSONHelp,虽然Microsoft 已经做的很好了.但是我想封装一套为自己开发的项目使用.方便 ...

  3. Python学习笔记——import模块

    OS模块 直接输出系统命令到屏幕,该方法获取的命令返回值不可被赋值给变量,输出结果受编码影响会乱码: import os os.system("ipconfig") 将执行获取的系 ...

  4. js验证身份证号码

    function IdentityCodeValid(code) { var city={11:"北京",12:"天津",13:"河北",1 ...

  5. Python爬虫从入门到放弃(十七)之 Scrapy框架中Download Middleware用法

    这篇文章中写了常用的下载中间件的用法和例子.Downloader Middleware处理的过程主要在调度器发送requests请求的时候以及网页将response结果返回给spiders的时候,所以 ...

  6. C++学习(三)入门篇——函数

    C++函数分两种:有返回值的和没返回值的 1.有返回值的函数 调用函数流程 如图,sqrt(6.25)为函数调用,被调用的函数叫做被调用函数,包含函数调用的函数叫做调用函数. 参数是发送给函数的信息, ...

  7. linux 的一些脑洞操作

    把当前文件夹的文件名用","连接成一行,或者将多行转变为一行 ls | paste -s -d "," # -s 选项将输入进行一次性粘贴 ls | xargs ...

  8. Python进阶——笔记1

    1.*args 的用法 *args 和 **kwargs 主要用于函数定义. 你可以将不定数量的参数传递给一个函数. 这里的不定的意思是:预先并不知道, 函数使用者会传递多少个参数给你, 所以在这个场 ...

  9. 创建PDF模板,java添加内容、导出下载PDF

    本文主要内容是:用java在pdf模板中加入数据,图片. 废话不多说,举个非常简单的例子: 首先创建word文档,导出PDF. 用 软件adobe acrobat打开,操作步骤如图: 在指定位置添加文 ...

  10. 懵懂oracle之存储过程3--JOB详解

    在前面学习了存储过程的开发.调试之后,我们现在就需要来使用存储过程了.简单的使用,像上篇<懵懂oracle之存储过程2>中提到的存储过程调用,我们可以将写好的存储过程在另一个PL/SQL块 ...