zoukankan      html  css  js  c++  java
  • 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的。

  • 相关阅读:
    数据库架构的演变
    一个简单的跨库事务问题
    一个优美的架构需要考虑的几个问题
    铁道部新客票系统设计
    详细介绍软件架构设计的三个维度
    单代号网络图
    分库分表带来的完整性和一致性问题
    软件架构设计箴言理解
    设计高并发的电子商店
    mysql之索引补充
  • 原文地址:https://www.cnblogs.com/33debug/p/6686243.html
Copyright © 2011-2022 走看看