前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++继承、虚函数、RTTI、友元类、异常处理

C++继承、虚函数、RTTI、友元类、异常处理

作者头像
歪歪梯
发布2020-08-17 17:35:20
7290
发布2020-08-17 17:35:20
举报
文章被收录于专栏:歪歪梯Club歪歪梯Club

继承

前面讲到c++的继承是子类在继承时声明继承的权限,之前描述有点不够准确。以下时书中提及的能继承的成员。

成员函数属性 ==当使用private继承时,父类的所有public成员在当前子类中会变为private。当使用protected继承时,父类的所有public成员在当前子类中会变为protected。==。

虚函数

c++中,被定义为虚函数的成员,能被子类重写,虚函数是用virtual修饰的函数。原理是每个有虚函数的类对象内部维护一个的虚方法表成员,记录包含的方法以及对象的类型信息,虚函数只有在运行期(runtime)才会去动态确定使用哪个实现方法 比如:

代码语言:javascript
复制
class SuperClass{
    public:
        SuperClass(const int & a){
            std::cout<<"superClass "<<a<<std::endl;
        }
        SuperClass(const SuperClass & another){
            std::cout<<"copy superClass "<<&another<<" "<<this<<std::endl;
        }
        void show(){
            std::cout<<"I am superClass "<<std::endl;
        }
        virtual void vShow(){
            std::cout<<"vShow I am superClass "<<std::endl;
        }
};
class SubClass : public SuperClass{
 public:
    SubClass(const int & a):SuperClass(a){//可以通过这样指定使用的父类构造函数
    }
    void show(){
        std::cout<<"I am subClass "<<std::endl;
    }
    virtual void vShow(){
        std::cout<<"vShow I am subClass "<<std::endl;
    }
};

上述代码定义了SuperClass和SubClass,并具备show方法和vShow方法,假设有如下代码

代码语言:javascript
复制
    SuperClass s1 = SubClass(1);
    //copy superClass 
    s1.show();//I am superClass 
    s1.vShow();//vShow I am superClass 

    SuperClass && s2 = SubClass(1);
    s2.show();//I am superClass 
    s2.vShow();//vShow I am subClass 

    SuperClass * s3 = new SubClass(1);
    s3->show();//I am superClass 
    s3->Show();//vShow I am subClass 

右边的注释为每个方法调用的输出,可以看到,如果使用普通变量定义来初始化子类对象,子类的对象可以作为父类对象使用,这时候因为会调用拷贝构造函数,最终变为一个新的父类对象,所以没有意义。 而如果通过引用,则不会执行拷贝构造。因为引用类型是父类型,在调用普通方法时,仍是父类方法,只有调用虚方法时,使用了真正的子类方法。而指针类型也是与引用类型类似。

析构函数与继承

c++中子类析构函数结束会自动调用父类析构函数。接下来看看继承下析构的表现,假设我们将析构改为如下。

代码语言:javascript
复制
class SuperClass{
    public:
       ~SuperClass(){
               std::cout<<"SuperClass destructor"<<std::endl;
       }
};
class SubClass : public SuperClass{
 public:
    ~SubClass (){
           std::cout<<"SubClass destructor"<<std::endl;
    }
};

执行代码

代码语言:javascript
复制
SuperClass && s = SubClass();
//SubClass destructor
//SuperClass destructor

证实引用类型会调用被引用的对象的真实类型的析构函数

代码语言:javascript
复制
SuperClass * s = new SubClass();
delete s;
//SuperClass destructor

对于new出来的堆对象进行delete删除时,只调用了指针类型对应的析构函数,因为delete是显示调用当前指针类型的析构函数处理,面对这种情况可以通过把父类的析构函数定义为虚函数,则delete调用时为调用虚函数,要去动态绑定会重新根据内存对象的类型选择子类的析构函数

代码语言:javascript
复制
class SuperClass{
    public:
       virtual ~SuperClass(){
               std::cout<<"SuperClass destructor"<<std::endl;
       }
};
class SubClass : public SuperClass{
 public:
    virtual ~SubClass (){
           std::cout<<"SubClass destructor"<<std::endl;
    }
};

此时再执行以下代码

代码语言:javascript
复制
SuperClass * s = new SubClass();
delete s;
//SubClass
//SuperClass destructor

发现已经被定向为子类的析构函数了

纯虚函数

在java中我们有接口的定义,接口定义的方法必须是抽象方法,要求子类必须实现,纯在抽象方法的类不能实例化。在c++中有对应的纯虚函数,具备纯虚函数的类不能进行实例化,纯虚函数指将虚函数赋值为0的函数,如

代码语言:javascript
复制
class A{
    virtual pureVirtualFunction() = 0;
}

类的提前声明

类与函数类似,都具备提前声明提高作用域的方法,用法如下

代码语言:javascript
复制
class B;
class A{
    B b;
}
class B{}

友元类

前面讲过友元函数的作用,类中的方法也可以作为友元函数看待,比如

代码语言:javascript
复制
class B;
class A{
    void show(B b){}
}
class B{
    friend void A::show(B b);
}

当我们想把整个类的所有成员函数都作为友元时,可以直接将类作为友元,如

代码语言:javascript
复制
class B;
class A{
    void show(B b){}
}
class B{
    friend class A;
}

RTTI

cpp中为了对强制转换进行更高一级的优雅限制,提供了RTTI(Runtime Type identify),中文叫运行时类型识别。我们先看看以前的强制类型转换

代码语言:javascript
复制
long a = 10l;
int * b = (int *) (&a);

这样可以将long类型指针强制转为int类型指针,但是这种转化方式是直接更改编译器对该内存空间的读取方式,而不是经过检查的,存在一定风险。为此,cpp提供了四大强制转化运算专门处理

dynamic_cast

dynamic_cast运算符,判断传入对象是否可以安全的转为给定的指针类型/引用(是否为该类父类指针或子类指针/该类父类引用或子类引用),可以则传递该对象地址/转化后的引用,否则返回空指针(对于引用类型则是抛出异常) ,要向下转化要求传入参数的类型对应的类中需要有虚函数,否则编译出错,因为虚方法表里包含了类型信息type_info,向下转型需要使用type_info来判断是否可以转型(动态联编),因而称为动态转化,向上不用(编译器已知继承关系),用法

代码语言:javascript
复制
SuperClass * ss = new SubClass();
SubClass* s = dynamic_cast<SubClass*>(ss );//向下转型,SuperClass中要有虚方法 

static_cast

static_cast与dynamic_cast用法相同,唯一区别是他没有动态检查,也就是向下转型不强制要求传入参数的类型对应的类中有虚函数。并且如果向下转型是错误的,也不会报错,static_cast与强制转化类似,将当前引用/指向的内存空间作为转化后的类型来用,这会导致一些不可知的错误,如读取从成员变量所对应的空间是别的用途或者未初始化的,当把较大的数据单元转化为较小的数据单元时,static_cast的处理时丢掉溢出的部分

const_cast

除了对继承关系的转化,还有const与volatile关键字的限定关系的转化。const_cast用来对指针/引用变量转化为,带const/volatile修饰的,或者完成取消const/volatile修饰。也就是用来消灭const限定和volatile限定的,因为const指针/引用只能赋值给const指针/引用。比如:

代码语言:javascript
复制
class root{
    public:
        int j;
}
const root * a = new root();
//a->j = 6; err
root * b = const_cast<root *>(a);
b->j = 6;//ok

reinterpret_cast

reinterpret_cast与static_cast类似,也是没有检查的转化,唯一区别是,reinterpret_cast是按照二进制来解释,也就是说,你甚至可以把对象类型cast为整形(因为按照二进制来解释,多的位丢掉)

throw与noexcept

c++中可以通过throw关键字抛出一个任意对象,程序会将其作为一个异常对象处理,处理步骤 1.查找被包围的匹配类型的catch块,有就跳到catch块代码 2.没有找到匹配的catch块,则调用terminate函数,一般编译器处理是调用abort函数,以异常情况结束程序 noexcept标明告诉编译器,本方法不会抛出异常,有写情况下能提高性能,同时c++中也有exception类,在头文件exception。其中有what()虚函数,返回一个const char *,一般通过重写该方法表示异常信息。代码示例

代码语言:javascript
复制
try{
    throw "I am string";
}catch(const char * msg){
    //code
}

final

与java类似,c++也有final,通过在类名后面或者虚函数后面加上final关键字代表禁止继承或者禁止重写,如

代码语言:javascript
复制
class A final{
    virtual void show() final{}
}

override

但子类编写与父类具备不同形参的同名虚方法时,编译器会认为是覆盖,将对子类隐藏父类的同名方法,为了加强对这种情况的检查,可以通过在子类方法后面加上override关键字,代表是重写父类方法而不是覆盖,此时如果形参类型不一致,会导致编译失败。如

代码语言:javascript
复制
class A{
    virtual void show(int a){}
};
class B : public A{
    virtual void show(int * p){}
};
//导致 new B()->show(1);不能调用,被隐藏

这时使用override,严格检查重写,发现父类没有存在形参为int *的show方法,编译出错

代码语言:javascript
复制
class A{
    virtual void show(int a){}
};
class B : public A{
    virtual void show(int * p) override{}
};
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 歪歪梯Club 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 虚函数
  • 析构函数与继承
  • 纯虚函数
  • 类的提前声明
  • 友元类
  • RTTI
    • dynamic_cast
      • static_cast
        • const_cast
        • reinterpret_cast
        • throw与noexcept
        • final
        • override
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档