c++笔记:虚函数必要但易忘的一些性质

前言:

老师留了一个作业,就是对虚函数的使用,没有什么难度,但就是对虚函数的性质用来用去,搞得我一次又一次的查资料,可能这也是每个人必走的吧,所以我准备分享一下我认为学习c++虚函数里面需要注意的性质以及用法。

 

总结:其实虚函数总结就一句话:使用基类的指针,指向派生类对象,调用虚函数的时候,最后调用的是派生类的函数。

 

一:动态绑定:

只有调用基类的指针引用调用虚函数时,才会发生动态绑定。

  • 例如:
Derived d;//Derived是派生类,Base是基类
Base *ptr=&d;//积累指针指向派生类的对象
Base &ref=d;//基类引用作为派生类的别名
Base b=d;//调用Base的拷贝函数用d构造b

 

  • 效果:
    ptr->函数名,调用派生类中的对应函数
    ref.函数名,掉用派生类中的对应函数
    b.函数名,调用基础类中的对应函数
    

      


 

二:静态动态连编:针对基类派生类有同名函数

  • 通过派生类对象访问同名函数。这是静态连编

  • 通过基类对象的指针访问同名函数。这也是静态连编

  • 通过基类对象的指针或引用访问同名虚函数,这是动态连编(老师喜欢考的)

    class P{public:f(){.....}};//father
    class C:public P{public:f(){....}};//son
    main(){
        P* ptr;
        P p;
        C c;
        ptr=&p;
        ptr->f();//调用P::f()
        
        ptr=&c;
        ptr->f();//调用P::f()
    }
    

      


注意:要是没有vitural,指针什么类型就调用什么类型

 

 

三:overeide和final:(这老忘,还是多记记才是)

  • override显式声明覆写
class A {
public:
  virtual void foo() {}
  void bar() {}
};
 
class B : public A {
public:
  void foo() const override { // 错误: B::foo 不覆写 A::foo
  }                           // (签名不匹配)
  void foo() override;   // OK : B::foo 覆写 A::foo
  void bar() override {} // 错误: A::bar 非虚
};
 
void B::foo() override {// 错误: override只能放到类内使用
}

  

 

 

  • 还有一种覆写方式,这好像是之前学习到的一个知识点,忘了,重新巩固一下。。。
    class Animal
    {
    public:
    	Animal();
    	~Animal();
    	virtual Animal& speak() {//可以只在父类中定义vitural
    		return *this;//注意一定要在每一个覆写的后面加上这个返回
    	}
    
    };
    
    class Dog:public Animal
    {
    public:
    	Dog();
    	~Dog();
    	Dog& speak() {
    		cout << "wang" << endl;
    		delete this;
    		return *this;
    	}
    };
    void test(Animal& p){//注意:覆写之后暂时还没有效果,必须要定义一个void函数来调用,当然是用下面的方式来调用啦
        p.speak();
    }
    

      


 

  • final 显式声明禁止覆写

 

struct Base {
    virtual void foo();
};
 
struct A : Base 
{ 
    void foo() final; // A::foo 被覆写且是最终覆写
    void bar() final; // 错误:非虚函数不能被覆写或是 final
};
 
struct B final : A // struct B 为 final,不能被继承
{
    void foo() override; // 错误: foo 不能被覆写,因为它在 A 中是 final
};

  

 

 

四:抽象类:

特征:

  • 抽象类不能实例化。

  • 抽象类可以包含抽象方法和抽象访问器。

  • 从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实际实现。

  • 必须是派生类来实现,而不是让基类实现

 

 

五:动态转换dynamic_cast:

使用情况:下面会提到上转和下转,与其类似


 

// A function for displaying a Shape object
void printObject(Shape &shape)
{
  cout << "The area is " 
       << shape.getArea() << endl;
    
    //这里就是判断是不是该类型是不是要转换的类型,当然必须用到指针
  Shape *p = &shape;
  Circle *c = dynamic_cast<Circle*>(p);
  // Circle& c = dynamic_cast<Circle&>(shape); 
  // 引用转换失败则抛出一个异常 std::bad_cast
  if (c != nullptr) // 转换失败则指针为空
  {
    cout << "The radius is " 
         << p1->getRadius() << endl;
    cout << "The diameter is " 
         << p1->getDiameter() << endl;
  }
}

  

 

六:基类和派生类向上和向下转换:(很重要,课设里面会经常用到)

  • 上转:upcasting : Assigning a pointer of a derived class type to a pointer of its base class type (将派生类类型指针赋值给基类类型指针)

    Shape* s = nullptr;
    Circle *c = new Circle(2);
    s = c; //OK,隐式上转
    

      


  • 下转:downcasting : Assigning a pointer of a base class type to a pointer of its derived class type. (将基类类型指针赋值给派生类类型指针)

Shape* s = new Circle(1);
Circle *c = nullptr;
c = dynamic_cast <Circle*> (s); //显式下转

 

好了,上面就是我对c++虚函数一些性质的简单总结和用法

posted @ 2020-04-22 23:22  焕不涣  阅读(312)  评论(0编辑  收藏  举报