欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

派生类详解

程序员文章站 2024-03-23 12:02:04
...

派生类:

一:相关概念
基类(父类,超类)
子类

父类一般定义一些公用的成员变量,成员函数。
子类:通过继承父类来构建新的类,写子类时只需写一些和子类相关的内容,所以,子类相比于父类有着更多的成员变量和成员函数。

定义子类的方式:

class 子类名:继承方式 父类名
//继承方式:public private protected

二:定义一个子类对象时,构造函数调用的顺序,析构函数调
用的顺序。

class B //父类
{
public:
       B()
       {
              cout << "调用了构造函数B " << endl;
       }
       ~B()
       {
              cout << "调用了析构函数B " << endl;
       }
};

class A:public B //子类
{
public:
       A(){ cout << "调用了构造函数A "<< endl; }
       ~A()
       {
              cout << "调用了析构函数A " << endl;
       }
};

A obj;//定义子类对象时需要调用父类和子类的构造函数。

//构造函数调用顺序:先父后子
//析构函数调用顺序:先子后父

派生类详解
三:等级访问问题(public private protected)

四:函数遮蔽问题

概述:在定义一个子类对象时,是可以调用父类的成员函数的(和访问等级和继承方式有关),例如:

class B
{
public:
       void fun()
       {
              cout << "调用了void fun()B " << endl;
       }
};
class A:public B
{
public:
};
int main()
{
     A obj; //定义子类对象
     obj.fun();//调用的是父类的成员函数
}

但是,如果在子类中,有一个和父类中相同的函数的话(函数名相同即可,不管形参个数),那么这个时候,定义一个子类对象时,所有的在父类中的同名函数就调用不了了,即被屏蔽了,例如:

class B
{
public:
       void fun()
       {
              cout << "调用了void fun()B " << endl;
       }
       void fun(int a)
       {
              cout << "调用了void fun(int a)B " << endl;
       }
};
class A:public B
{
public:
       A()
       {
              cout << "调用了构造函数A "<< endl;
       }
       ~A()
       {
              cout << "调用了析构函数A " << endl;
       }
       void fun(int a,int b)
       {
              cout << "调用了void fun()A " << endl;
       }
};
int main()
{
       A obj;
       obj.fun(); //错误,被屏蔽了
       obj.fun(1); //错误,被屏蔽了
}

当然,如果这个时候,在子类中有一个和父类中的同名函数的话(形参也相同),那么这个时候调用的当然也就是子类中的函数,例如:

class B
{
public:
       void fun()
       {
              cout << "调用了void fun()B " << endl;
       }
};
class A:public B
{
public:
       void fun()
       {
              cout << "调用了void fun()A " << endl;
       }
};
int main()
{
       
       A obj;
       obj.fun(); //可以调用,因为调用的是子类中的函数
}

如果,在子类中有了和父类中相同的函数名的话,那么这个时候想调用父类中的同名函数(调用那些被屏蔽的同名函数,)的话,有两种办法。

办法一:
使用:using
在c++11中为using定义了一种含义,即除了命名空间外的含义。

class B
{
public:
       B()
       {
              cout << "调用了构造函数B " << endl;
       }
       ~B()
       {
              cout << "调用了析构函数B " << endl;
       }
       void fun()
       {
              cout << "调用了void fun()B " << endl;
       }
       void fun(int a)
       {
              cout << "调用了void fun(int a)B " << endl;
       }
};
class A:public B
{
public:
       A()
       {
              cout << "调用了构造函数A "<< endl;
       }
       ~A()
       {
              cout << "调用了析构函数A " << endl;
       }
       void fun(int a,int b)
       {
              cout << "调用了void fun()A " << endl;
       }
public:
       using B::fun; //让父类的同名函数在子类中暴露出来
};
int main()
{      
       A obj;
       obj.fun();//正确
       obj.fun(1);//正确
}

五:派生类对象模型概述

派生类是由两部分组成的,一部分是基类部分,一部分是派生出来的(除了基类部分的成员)
所以对于一个父类指针可以new一个子类对象来说,因为子类对象含有父类部分,所以这个子类是可以当作父类来用的,因此,也解释了,为什么一个父类指针可以new一个子类对象。

所以有时候,一个子类指针是可以赋值给父类指针的。

class A
{
public:
       A()
       {
              cout << "调用了A()" << endl;
       }
       ~A()
       {
              cout << "调用了~A()" << endl;
       }
       
};
class B:public A
{
public:
       B()
       {
              cout << "调用了B()" << endl;
       }
       ~B()
       {
              cout << "调用~B()"<<endl;
       }
              
};
int main()
{
       B *p = new B;
       A *p1 = new A;
       p1 = p; //正确
}

六:派生类的构造函数

定义一个派生类对象时,基类构造函数用来初始化基类部分,派生类构造函数用来初始化派生类部分。

但是,如果基类没有无参的构造函数,而是有有参的构造函数(不管是几个有参的构造函数)时,就面临着参数传递问题,即,如果只有默认构造该函数,那么我们不用手动调用基类的构造函数,就可以完成基类和子类部分成员的初始化,但是,如果只有有参的构造函数的话,就需要自己手动调用父类的构造函数了。

class A
{
public:
       A(int i)
       {
              cout << "调用了A()" << endl;
       }
       A(int a,int b)
       {
              cout << "调用了A(int a,int b) "<< endl;
       }
       ~A()
       {
              cout << "调用了~A()" << endl;
       }
public:
       int M_A;
};
class B:public A
{
public:
       B():A(2,8) //手动调用
       {
              
              cout << "调用了B()" << endl;
       }
       ~B()
       {
              cout << "调用~B()"<<endl;
       }      
public:
       int M_B;
};
int main()
{
       B b;
}

七:既是父类又是子类的类。

八:不想当基类的类
c++11 final的使用
使用方法:在类名后边加final


class A final
{
public:
       A()
       {
              cout << "调用了A()" << endl;
       }
       ~A()
       {
              cout << "调用了~A()" << endl;
       }
public:
       int M_A;
};
class B:public A //错误
{
public:
       B()
       {
              
              cout << "调用了B()" << endl;
       }
       ~B()
       {
              cout << "调用~B()"<<endl;
       }      
public:
       int M_B;
};

说明:如果类B不想当基类,那么同样可以在类B名后边加final

九:静态类型和动态类型

静态类型:也就是声明时的类型。编译时已知。
动态类型:指的是内存中的类型。
只有基类指针存在静态类型和动态类型不一样的情况。

十:派生类向基类的隐式类型转换,父类与子类之间的拷贝与赋值
这一部分,可按派生类模型来理解。

相关标签: C++98,11,14,17