C++学习笔记 (五) ---- 继承与派生

①、继承可以理解为一个类从另一个类获取成员变量和成员函数的过程,被继承的类称为基类,继承的类称为派生类。

派生类除了拥有基类成员,还可以自定义新成员。

#include<iostream>
using namespace std;

//基类 Pelple
class People{
  public:
    void setname(char *name);
    void setage(int age);
    char *getname();
    int getage();
  private:
    char *m_name;
    int m_age;
};
void People::setname(char *name){ m_name = name; }
void People::setage(int age){ m_age = age; }
char* People::getname(){ return m_name; }
int People::getage(){ return m_age;}

//派生类 Student
class Student: public People{         //Student类继承People类,public方式;
  public:
    void setscore(float score);
    float getscore();
  private:
    float m_score;
};
void Student::setscore(float score){ m_score = score; }
float Student::getscore(){ return m_score; }

int main(){
    Student stu;
    stu.setname("小明");
    stu.setage(16);
    stu.setscore(95.5f);
    cout<<stu.getname()<<"的年龄是 "<<stu.getage()<<",成绩是 "<<stu.getscore()<<endl;

    return 0;
}

由此可看出继承的一般方式为:

class 派生类名:[继承方式] 基类名{
    //派生类新增成员;
};

继承方式有:public(公有)、private(私有)和 protected(受保护),默认 private

类成员的访问权限:publicprotected > private

其中,protected 成员也是和 private 成员类似,也不能通过对象访问;但是在继承的时候,基类的 protected 成员可以在派生类中使用,但是 private 成员不行。

不同继承方式对不同属性成员的影响

②、继承中的名字遮蔽

如果派生类中的成员和基类中的成员重名,那么会遮蔽从基类继承过来的成员,即使用该成员时实际上使用的是派生类中的成员。因此,基类和派生类中的同名函数不构成重载。例:

#include<iostream>
using namespace std;

//基类Base
class Base{
public:
    void func();
    void func(int);
};
void Base::func(){ cout<<"Base::func()"<<endl; }
void Base::func(int a){ cout<<"Base::func(int)"<<endl; }

//派生类Derived
class Derived: public Base{
public:
    void func(char *);
    void func(bool);
};
void Derived::func(char *str){ cout<<"Derived::func(char *)"<<endl; }
void Derived::func(bool is){ cout<<"Derived::func(bool)"<<endl; }

int main(){
    Derived d;
    d.func("c.biancheng.net");
    d.func(true);
//    d.func();  //compile error
//    d.func(10);  //compile error
    d.Base::func();
    d.Base::func(100);

    return 0;
}

上例中,构成重载的只是 Base 类的两个 func 或者 Derive 类的两个 func。所以要是想访问基类的同名函数,需要指定作用域,如 28、29 两行所示。

③、派生类的构造函数

类的构造函数不能被继承,所以在初始化基类的成员变量时,是在派生类的构造函数中调用基类的构造函数来完成初始化。

#include<iostream>
using namespace std;

//基类People
class People{
  protected:
    char *m_name;
    int m_age;
  public:
    People(char*, int);
};
People::People(char *name, int age): m_name(name), m_age(age){}

//派生类Student
class Student: public People{
  private:
    float m_score;
  public:
    Student(char *name, int age, float score);
    void display();
};

//People(name, age)就是调用基类的构造函数
Student::Student(char *name, int age, float score): People(name, age), m_score(score){ }
void Student::display(){
    cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<"。"<<endl;
}

int main(){
    Student stu("小明", 16, 90.5);
    stu.display();

    return 0;
}

像 24 行代码一样,People(name, age) 就是调用基类的构造函数,并将 name 和 age 作为实参传递给它,m_score(score) 是派生类的参数初始化列表,它们之间用逗号隔开。

注: 多层派生中,派生类的构造函数中只能调用直接基类的构造函数,不能调用间接基类的。

同理,析构函数也是不能被继承的,并且因为每个类只有一个析构函数,所以派生类的析构函数不需要显式地调用基类的构造函数,编译器会自动选择。

另外,析构函数的执行顺序和构造函数的执行顺序也刚好相反:创建派生类对象时,构造函数的执行顺序和继承顺序相同,即先执行基类构造函数,再执行派生类构造函数。而销毁派生类对象时,析构函数的执行顺序和继承顺序相反,即先执行派生类析构函数,再执行基类析构函数。

④、C++指针可以突破权限限制

万能的指针是可以穿透地址,随意修改类的成员,即使是 private、const 成员也无济于事。

⑤、C++向上转型(将派生类赋值给基类)

C/C++中对不同类型的变量进行赋值时,编译器会将其转化成同类型,然后再赋值。同理,类也是一种数据类型,也可以进行数据转化,这种转化一般只发生在基类和派生类之间。将派生类赋值给基类称为向上转型,将基类赋值给派生类称为向下转型。

向上转型很安全,由编译器完成,向下转型有风险,需要程序员手动干预。

#include <iostream>
using namespace std;

//基类
class A{
  public:
    A(int a);
    void display();
    int m_a;
};
A::A(int a): m_a(a){ }
void A::display(){
    cout<<"Class A: m_a="<<m_a<<endl;
}

//派生类
class B: public A{
  public:
    B(int a, int b);
    void display();
    int m_b;
};
B::B(int a, int b): A(a), m_b(b){ }
void B::display(){
    cout<<"Class B: m_a="<<m_a<<", m_b="<<m_b<<endl;
}

int main(){
    A a(10);
    B b(66, 99);
    //赋值前
    a.display();
    b.display();
    cout<<"--------------"<<endl;
    //赋值后
    a = b;
    a.display();
    b.display();

    return 0;
}

//运行结果:
Class A: m_a=10
Class B: m_a=66, m_b=99
----------------------------
Class A: m_a=66
Class B: m_a=66, m_b=99

将派生类对象赋值给基类对象时,会舍弃派生类新增的成员:

注:同一基类的不同派生类对象之间也不能赋值

猜你喜欢

转载自blog.csdn.net/hsl416604093/article/details/83586415
今日推荐