C++中的继承详解(3)作用域与重定义,赋值兼容规则
1、作用域与重定义(同名隐藏)
一样的,先上代码
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
如果C类定义不传参的同名函数更能说明情况:
class C :public A, public B
{
public:
int _data;
void a()
{
cout << "C" << endl;
}
};
int main()
{
C c;
c.a();
getchar();
return ;
}
编译通过,运行输出:C
通过以上的例子,完全可以说明,当我们在派生类中定义一个同名函数的时候,编译器是将同名函数隐藏了,不管参数表是否相同。即不会构成函数重载,直接将基类函数覆盖。
那么问题来了,为什么不会构成函数重载呢?
一定要注意,函数重载的条件是在同一个作用域中才会构成函数重载,而派生类和基类是两个类域,一定不会构成函数重载的。
当然,我们还有另外一个方法访问同名时基类中的成员即
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. 注意在实际中在继承体系里面最好不要定义同名 的成员 。
2、继承与转换——赋值兼容规则(public继承为例)
派生类和基类之间的特殊关系为:
1.派生类对象可以赋值给基类对象 ;基类对象不能赋值给派生类。
2.基类指针可以在不进行显示类型转换的情况下指向派生类对象 。
3.基类引用可以在不进行显示类型转换的情况下引用派生类对象,但是基类指针或引用只能用于调用基类的方法,不能用基类指针或引用调用派生类的成员及方法。
void FunTest(const Base&d)
{}
void FunTest1(const Derive&d)
{}
int main()
{
Derive d;
Base b();
b = d;//可以
d = b;//不行,访问的时候会越界
//上面已经解释过了
FunTest(b);
FunTest(d);
FunTest1(b); //报错
FunTest1(d);
Base* pBase = &d; //父类指针/或引用可以直接指向子类对象,但只能访问子类中继承父类的部分
Derive* pD = &b;//报错 子类的指针/引用不能直接指向父类对象
//如果非要这么做只能通过强制类型转换
Derive* pD = (Derive*)&b;//如果访问越界,程序会崩溃
}
通常,C++要求引用和指针类型与赋给的类型匹配,但这一规则对继承来说是个例外。但是这个例外是单向的,即仅仅不可以将基类对象和地址赋给派生类引用和指针。如果允许基类引用隐式的引用派生类对象,则可以使用基类引用为派生类对象调用基类的方法,因为派生类继承了基类的方法,所以这样不会出现问题。但是如果可以将基类对象赋给派生类引用,那么派生类引用能够为积累对象调用派生类方法,这样做会出现问题,例如:用基类对象调用派生类中新增的方法,是没有意义的,因为基类对象中根本没有派生类的新增方法。
3、友元与继承
class Person
{
friend void Display(Person &p, Student&s);
protected:
string _name; // 姓名
};
class Student : public Person
{
protected :
int _stuNum; // 学号
};
void Display(Person &p, Student &s)
{
cout << p._name << endl;
cout << s._name << endl;
cout << s._stuNum << endl;
}
void TestPerson1()
{
Person p;
Student s;
Display(p, s);
}
友元关系不能继承, 也就是说基类友元不能访问子类私有和保护成员 。友元只是能访问指定类的私有和保护成员的自定义函数,不是被指定类的成员,自然不能继承。
使用友元类时应该注意:
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3)友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明
(4)友元可以访问类的私有成员。
(5)友元只能出现在类定义内部,友元声明可以在类中的任何地方,一般放在类定义的开始或结尾。
(6)友元可以是普通的非成员函数,或前面定义的其他类的成员函数,或整个类。
(7)类必须将重载函数集中每一个希望设为友元的函数都声明为友元。
(8)友元关系不能继承,基类的友元对派生类的成员没有特殊的访问权限。如果基类被授予友元关系,则只有基类具有特殊的访问权限。该基类的派生类不能访问授予友元关系的类。
4、继承与静态成员
了解这个问题之前,我们先确定一件事情,那就是,在基类和派生类中,静态成员和静态成员函数共用同一段空间。 我们在以前的学习过程中都知道,类的模型是所有对象的数据成员单独存储,但是所有成员函数和静态成员是共用一段空间的
class Person
{
public :
Person() {++ _count; }
protected :
string _name ; // 姓名
public :
static int _count; // 统计人的个数。
} ;
int Person: : _count = ;
class Student : public Person
{
protected :
int _stuNum ; // 学号
} ;
class Graduate : public Student
{
protected:
string _seminarCourse; // 研究科目
} ;
void TestPerson1()
{
Student s1;
Student s2;
Student s3;
Graduate s4;
cout<<"人数: "<<Person: : _count<<endl;
Student : : _count = ;
cout<<"人数: "<<Person: : _count<<endl;
}
基类定义了 static成员 , 则整个继承体系里面只 有一个这样的成员 。 无论派生出多少个子类, 都只有 一个static成员 实例。父类的static变量和函数在派生类中依然可用,但是受访问性控制(比如,父类的private域中的就不可访问)。而且对static变量来说,派生类和父类中的static变量是共用空间的,这点在利用static变量进行引用计数的时候要特别注意。派生类的friend函数可以访问派生类本身的一切变量,包括从父类继承下来的protected域中的变量。但是对父类来说,他并不是friend的。
C++中的继承详解(3)作用域与重定义,赋值兼容规则的更多相关文章
- C++中的继承(3)作用域与重定义,赋值兼容规则
作用域与重定义(同名隐藏) 一样的,先上代码 1 class A 2 { 3 public: 4 int a_data; 5 void a() 6 { 7 cout << "A& ...
- angularJS中$apply()方法详解
这篇文章主要介绍了angularJS中$apply()方法详解,需要的朋友可以参考下 对于一个在前端属于纯新手的我来说,Javascript都还是一知半解,要想直接上手angular JS,遇到的 ...
- spring在IoC容器中装配Bean详解
1.Spring配置概述 1.1.概述 Spring容器从xml配置.java注解.spring注解中读取bean配置信息,形成bean定义注册表: 根据bean定义注册表实例化bean: 将bean ...
- 「万字图文」史上最姨母级Java继承详解
摘要:继承是面向对象软件技术中的一个概念.它使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用. 本文分享自华为云社区<「万字图文」史上最姨母级Java继承详解丨[奔跑吧!JAVA] ...
- 【转载】C/C++中extern关键字详解
1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern ...
- Android中Service(服务)详解
http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...
- [原创]JavaScript继承详解
原文链接:http://www.cnblogs.com/sanshi/archive/2009/07/08/1519036.html 面向对象与基于对象 几乎每个开发人员都有面向对象语言(比如C++. ...
- Android中mesure过程详解
我们在编写layout的xml文件时会碰到layout_width和layout_height两个属性,对于这两个属性我们有三种选择:赋值成具体的数值,match_parent或者wrap_conte ...
- iOS中—触摸事件详解及使用
iOS中--触摸事件详解及使用 (一)初识 要想学好触摸事件,这第一部分的基础理论是必须要学会的,希望大家可以耐心看完. 1.基本概念: 触摸事件 是iOS事件中的一种事件类型,在iOS中按照事件划分 ...
随机推荐
- 【转】Hibernate和IBatis对比
原文地址:http://blog.csdn.net/ya2dan/article/details/7396598 项目也做过几个, 使用IBatis就做一个项目, 基本上都是使用Hibernate, ...
- Python入门教程(2)
人生苦短,我玩蛇0.0! Python(英语发音:/ˈpaɪθən/), 是一种面向对象.解释型计算机程序设计语言,由Guido van Rossum于1989年底发明,第一个公开发行版发行于1991 ...
- 如何用好消息推送为app拉新、留存、促活
作为移动端APP产品运营最重要的运营手段,JPush消息推送被越来越多的APP厂商所重视,在信息泛滥的移动互联网时代,手机APP应用安装得越来越多,小小的手机屏幕每天收到的消息推送也越来越多,站在用户 ...
- 令人眼前一亮的下拉式终端 Tilda & Guake
前言 老夫是 Linux 的老用户. 大一的时候某不方便透露姓名的校内组织给了一个 Fedora 13 的安装光盘,然后老夫学会了重装 Windows. 大二的时候知道了 Ubuntu ,开始在虚拟机 ...
- 使用java.util.Properties类读写配置文件
J2SE 1.5 以前的版本要求直接使用 XML 解析器来装载配置文件并存储设置,虽说也并非难事,相比 java.util.Properties却要做额外的解析工作.而java.util.Proper ...
- File类和时间类的两道综合练习
练习1: 获取指定目录下(包含子目录)所有的某一种类型的文件 分析: 1.指定路径并获取其下的文件对象 2.要判断给定的目录是否为空 3.要判断给定路径下获取的目录是否为空 4.判断是否是某种文件 5 ...
- 2016: [Usaco2010]Chocolate Eating
2016: [Usaco2010]Chocolate Eating Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 224 Solved: 87[Su ...
- UNIX 网络编程知识,函数积累
1: fprintf()#include <stdio.h> int fprintf( FILE *stream, const char *format, ... );fprintf()函 ...
- ubuntu 虚拟机vm virtualbox 不能打开 win7
ubuntu某方面总有些不便,下载个虚拟机装个win7 但是第二次打开的时候就出现了安装是的场景: 原因很简单: 是因为安装了之后没有把win7的镜像文件移除,每次打开时会检测cd/dvd文件/(is ...
- Java中反射与常用方法
java通常是先有类再有对象,有对象我就可以调用方法或者属性. 反射其实是通过Class对象来调用类里面的方法.通过反射可以调用私有方法和私有属性.大部分框架都是运用反射原理. 如何获得Class ...