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类定义不传参的同名函数更能说明情况:

 1 class C :public A, public B
 2 {
 3 public:
 4     int _data;
 5     void a()
 6     {
 7       cout << "C" << endl;
 8     }
 9 };
10 int main()
11 {
12     C c;
13     c.a();
14     getchar();
15     return 0;
16 }

编译通过,运行输出: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.基类引用可以在不进行显示类型转换的情况下引用派生类对象,但是基类指针或引用只能用于调用基类的方法,不能用基类指针或引用调用派生类的成员及方法。

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

3、友元与继承

 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)友元关系不能继承,基类的友元对派生类的成员没有特殊的访问权限。如果基类被授予友元关系,则只有基类具有特殊的访问权限。该基类的派生类不能访问授予友元关系的类。
4、继承与静态成员

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

 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的。

posted @ 2017-04-11 12:46  滴巴戈  阅读(1368)  评论(0编辑  收藏  举报