我是靠谱客的博主 合适过客,最近开发中收集的这篇文章主要介绍C++ primer plus学习笔记(三)——继承、虚函数、RTTI、友元类、异常、final,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

C++ primer plus学习笔记(三)——继承、虚函数、RTTI、友元类、异常、final

    • 继承
    • 虚函数
    • 析构函数与继承
    • 纯虚函数
    • 类的提前声明
    • 友元类
    • RTTI
      • dynamic_cast
      • static_cast
      • const_cast
    • reinterpret_cast
    • throw与noexcept
    • final
    • override

继承

前面讲到c++的继承是子类在继承时声明继承的权限,之前描述有点不够准确。以下时书中提及的能继承的成员。
成员函数属性
当使用private继承时,父类的所有public成员在当前子类中会变为private。当使用protected继承时,父类的所有public成员在当前子类中会变为protected。

虚函数

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

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方法,假设有如下代码

	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++中子类析构函数结束会自动调用父类析构函数。接下来看看继承下析构的表现,假设我们将析构改为如下。

class SuperClass{
    public:
       ~SuperClass(){
       		std::cout<<"SuperClass destructor"<<std::endl;
	   }
};
class SubClass : public SuperClass{
 public:
    ~SubClass (){
       	std::cout<<"SubClass destructor"<<std::endl;
	}
};

执行代码

SuperClass && s = SubClass();
//SubClass destructor
//SuperClass destructor

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

SuperClass * s = new SubClass();
delete s;
//SuperClass destructor

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

class SuperClass{
    public:
       virtual ~SuperClass(){
       		std::cout<<"SuperClass destructor"<<std::endl;
	   }
};
class SubClass : public SuperClass{
 public:
    virtual ~SubClass (){
       	std::cout<<"SubClass destructor"<<std::endl;
	}
};

此时再执行以下代码

SuperClass * s = new SubClass();
delete s;
//SubClass
//SuperClass destructor

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

纯虚函数

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

class A{
    virtual pureVirtualFunction() = 0;
}

类的提前声明

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

class B;
class A{
	B b;
}
class B{}

友元类

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

class B;
class A{
	void show(B b){}
}
class B{
	friend void A::show(B b);
}

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

class B;
class A{
	void show(B b){}
}
class B{
	friend class A;
}

RTTI

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

long a = 10l;
int * b = (int *) (&a);

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

dynamic_cast

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

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指针/引用。比如:

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 *,一般通过重写该方法表示异常信息。代码示例

try{
	throw "I am string";
}catch(const char * msg){
	//code
}

final

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

class A final{
	virtual void show() final{}
}

override

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

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

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

class A{
	virtual void show(int a){}
};
class B : public A{
	virtual void show(int * p) override{}
};

更多文章,请搜索公众号歪歪梯Club
更多资料,请搜索公众号编程宝可梦

最后

以上就是合适过客为你收集整理的C++ primer plus学习笔记(三)——继承、虚函数、RTTI、友元类、异常、final的全部内容,希望文章能够帮你解决C++ primer plus学习笔记(三)——继承、虚函数、RTTI、友元类、异常、final所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部