C++继承

继承

派生类通过使用类派生列表指出它是从哪个基类继承而来,类派生列表的形式是:首先是一个冒号,后面紧跟逗号分隔的基类列表,每个基类前面可以有一个派生访问说明符。派生访问说明符的目的是控制派生类用户(包括派生类的派生类以及派生类的实例)对于基类成员的访问权限。

class Person
{
public:
    void publcRun()
    {
        std::cout << "publcRun" << std::endl;
    };
protected:
    void protectedRun()
    {
        std::cout << "protectedRun" << std::endl;
    }
private:
    void privateRun()
    {
        std::cout << "privateRun" << std::endl;
    }
};

class Worker1 :public Person
{
  
};

class Worker2:protected Person
{

};

class Worker3 :private Person
{
public:
    void workRun()
    {
        protectedRun();
    }
};


class Worker4: Worker3
{
public:
    void workRun()
    {
        protectedRun();//错误:Worker3的派生类不能访问Worker3的基类成员
    }
};
 
int main(void)
{
    Worker1().publcRun();//正确
    Worker2().publcRun();//错误
    Worker3().publcRun();//错误
    Worker4().publcRun();//错误
    system("pause");
    return 0;
}

派生类可以继承定义在基类中的成员,但是派生类的成员函数不一定有权访问从基类继承而来的成员,派生类只能访问基类中公有的和受保护的成员。

类型转换与继承

一个基类对象可能是派生类对象的一部分,也可能不是,所以基类向派生类不存在隐式类型转换。

派生类向基类的类型转换只对指针或引用有效,因为派生类向基类的转换允许我们给基类的拷贝或移动操作传递一个派生类的对象,这些操作不是虚函数,所以会执行基类中定义的那个,基类的构造函数只会处理自己的成员,最终造成派生类中的成员被切掉。

class Person
{
public:
    virtual void run()
    {
        std::cout << "person run..." << std::endl;
    };
};

class Worker :public Person
{
public:
    virtual void run() override
    {
        std::cout << "worker run..." << std::endl;
    };
};
 
int main(void)
{
    Person* person =new Worker();
    person->run();//worker run...

    Person&& person2 = Worker();
    person2.run();//worker run...

    Person person = Worker();
    person.run();//person run...
    system("pause");
    return 0;
}

派生类向基类转换的可访问性

派生类向基类的类型转换也可能会由于访问受限而变的不可行:

  • 只有派生类公有地继承基类时,用户代码才能使用派生类向基类转换。
  • 不论派生类以什么方式继承基类,派生类的成员函数和友元都能使用派生类向基类转换。
  • 如果派生类继承基类的方式是公有的或受保护的,则派生类的派生类的成员和友元可以使用派生类向基类的转换,否则如果继承方式是私有的,则不能使用。

友元与继承

就像友元关系不能传递一样,友元关系同样也不能继承,基类的友元在访问派生类成员时不具有特殊性,类似的,派生类的友元也不能随意访问基类的成员。

class Person 
{
    friend class Worker;
    const char* name = "person";
};

class Student:public Person
{
    int height = 100;
};

 
class Worker
{
public:
    void printPerson(Person& person)
    {
        std::cout << person.name << std::endl;
    }
    void printStudent(Student& student)
    {
        std::cout << student.name << std::endl;//正确,基类的友元可以访问派生类中的基类成员
    }
};

class Worker2:public Worker
{
public:
    void printPerson2(Person& person)
    {
        std::cout << person.name << std::endl;//错误,不能继承友元
    }
};

防止继承的发生

在类名后跟一个关键字final可以防止继承发生:

class Person final
{
};

//Person是final的,不能继承
class Worker :public Person
{

};

名字冲突与继承

如果在派生类中定义的名字与基类的名字冲突,则派生类中的名字将隐藏基类中的名字,可以通过作用域运算符来使用一个被隐藏的基类成员。

在派生类中定义与基类同名但是形参列表不一致的函数不会发生重载,而是会隐藏基类的函数,虚函数也是如此。如果派生类希望基类的同名函数对于它是可见的,可以使用using指定一个名字把基类的所有同名函数添加到派生类作用域中。

class Person
{
public:
    int height_=10;
    void func()
    {
        std::cout << "person func"<< std::endl;
    }
};


class Worker:public Person
{
public:
    int height_ = 100;
    void func()
    {
        std::cout << "worker func" << std::endl;
    }
};



int main(void)
{
    Worker worker;
    std::cout << worker.height_ << std::endl;//100
    std::cout << worker.Person::height_ << std::endl;//10
    worker.func();//worker
    worker.Person::func();//person
    system("pause");
    return 0;
}

派生类析构函数

在定义派生类的拷贝/移动操作时,需要调用基类的拷贝/移动操作,但是析构函数则不一样,对象的基类部分是隐式销毁的,所以派生类析构函数只负责销毁由派生类直接分配的资源。

在构造函数和析构函数中调用虚函数

在执行基类的构造函数时,该对象的派生类部分是未被初始化状态的,类似的,执行基类的构造函数时,派生类部分已经被销毁了,这个时候如果要调用虚函数,应该指定与构造函数或析构函数所属类型相对应的虚函数版本,否则会访问未初始化或已销毁的成员,导致程序出错。

你可能感兴趣的:(C++继承)