C++--多态--重载、虚函数、隐藏的比较(二版)

多态

概念

多态:多种形态。去完成某个行为,不同的对象会产生不同的结果。

这里以买票为例,学生买票,成人买票,军人买票。多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。Student继承了 Person。Person对象买票全价,Student对象买票半价。

构成多态的条件

1.通过父类的指针或引用调用虚函数。

2.虚函数重写。

虚函数

在函数声明的返回值前加virtual 就是虚函数

必须是成员函数才可以带virtual

class Person 
{
public:
    virtual void BuyTicket() { cout << "买票-全价" << endl;}
};

当一个类的某个函数被说明为 virtual ,则在该类的所有派生类中的同原型函数都是虚函数

虚函数的重写

子类中有一个跟父类完全相同的虚函数(返回值类型、函数名字、参数列表完全相同)。

注:不符合重写才是隐藏关系。

class Person
{
public:
    virtual void Buyticket()
    {
        cout<<"买票--全价"<<endl;
    }
};
class Student :public Person
{
public:
    virtual void Buyticket()
    {
        cout<<"买票--半价"<<endl;
    }
};
void Func(Person& p)
{
    p.Buyticket();
}
int main()
{
    Person a;
    Student b;
    Func(a);
    Func(b);
    return 0;
}

虚函数重写的两个特例

1.如果父基的析构函数为虚函数,此时子类析构函数只要定义,无论是否加virtual关键字, 都与父类的析构函数构成重写。编译器对析构函数做了特殊处理,统一析构函数名字为destructor。

子类虚函数不加virtual,依旧构成重写。(ps:最好加上)

析构函数重写的意义

释放父类指针时能正确释放子类对象,指向的对象是谁,就调用谁的析构函数。

class Person {
public:
 virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:
 virtual ~Student() { cout << "~Student()" << endl; }
};

2.重写的协变。返回值可以不同,但必须是父子关系的指针或引用。任意父子关系即可

class A{};
class B:public A{};

class Person
{
public:
    virtual A* func()
    {
        return new A;
    }
}
class Student:public Person
{
public:
    virtual B* func()
    {
        return new B;
    }
}

 多态考题

class A
{
public:
	virtual void func(int val = 1){ std::cout << "A->" << val << std::endl; }
	virtual void test(){ func(); }
};

class B : public A
{
public:
    void func(int val = 0){ std::cout << "B->" << val << std::endl; }
};

int main(int argc, char* argv[])
{
	B*p = new B;
	p->test();
	return 0;
}

这里输出结果为 B->1

首先p是一个指向B对象的指针,调用test函数 由于test没有参数(默认有this指针),相当于p指针传给this指针,而p是一个B对象的指针,所以调用了B的func()函数,打印了B->

至于val,虚函数重写是接口继承,重写实现。即{ }内的内容是重写的 { }外继承了A中的func()

所以val初始化为了1,打印B->1

class A
{
public:
	virtual void func(int val){ std::cout << "A->" << val << std::endl; }
	void test(){ func(1); }
};

class B : public A
{
public:
	void func(int val){ std::cout << "B->" << val << std::endl; }
};

int main(int argc, char* argv[])
{
	A*p = new B;

	p->test();
	return 0;
}

 这里输入依旧为B->1 因为p是指向B对象的指针,A*仅是改变了该指针的访问形式。对象是谁,就去调用谁的虚函数。

 求输出结果

using namespace std;
class A{
public:
 A(char *s) { cout<<s<<endl; }
 ~A(){}
};
class B:virtual public A
{
public:
 B(char *s1,char*s2):A(s1) { cout<<s2<<endl; }
};
class C:virtual public A
{
public:
 C(char *s1,char*s2):A(s1) { cout<<s2<<endl; }
};
class D:public B,public C
{
public:
 D(char *s1,char *s2,char *s3,char *s4)
        :B(s1,s2)
        ,C(s1,s3)
        ,A(s1)
    { 
        cout<<s4<<endl;
    }
};
int main() {
 D *p=new D("class A","class B","class C","class D");
 delete p;
 return 0;
}

按照声明顺序去调用。A虽然没有显示的被继承,但按理来说A是最先被继承的。A的初始化里面B有,C也有,但在D中A是独一份的,所以应该由D来调用A的初始化以避免二义。A初始化之后,按照继承顺序先构造B,再构造C,此时的两者中的A就不会在初始化了。

答案是 class A         class B        class C         class D

 多态简答

一、inline函数可以是虚函数吗?

内敛函数没有地址,在调用处展开,多态是运行起来后去虚函数表里去找,内联函数在运行前就确定了,所以多态调用时inline失效。但是inline只是建议,当普通调用时,inline依然生效。

二、静态成员函数可以是虚函数吗?

不能。静态成员函数没有this指针。可以通过类域调用。静态成员函数可以认为是在编译时决议,虚函数是为了实现多态,多态都是运行时去找虚表决议的。

三、构造函数可以是虚函数吗?

不能。虚表是编译时就生成好的。对象中的虚表指针都是在构造函数初始化列表才初始化的。虚表没初始化好,做不了多态。拷贝构造也是构造,也不能是虚函数。

四、赋值运算符重载可以是虚函数吗?

可以。但是如果派生类中使用赋值操作符,传入的参数一定是派生类对象。那参数就不同,构不成函数重写。所以可以但没用。

五、析构函数可以是虚函数吗?

可以。而且最好把基类的析构函数定义成虚函数。

六、对象访问普通函数快还是虚函数更快?

首先如果是普通对象,是一样快的。如果是指针 对象或者是引用对象,则调用的普通函数快,因为构成多态,运行时调用虚函数需要到虚函 数表中去查找。

七、虚函数表是在什么阶段生成的?存在于哪里?

编译阶段就生成好的,存在代码段。构造函数初始化列表阶段初始化的是虚函数表指针,对象中存的也是虚函数表指针。

 重载、隐藏、重写

重载的构成条件:在同一个作用域下的函数,函数名相同,参数列表不同构成重载;

隐藏的构成条件:派生类中含有与基类同名的方法,编译器会优先选择使用派生类中同名方法,看上去派生类将基类的同名方法隐藏。隐藏也叫重定义。

重写构成条件:

  • 方法A在基类中是虚函数
  • 子类中存在一个与父类的那个虚函数返回值、参数列表相同的函数B,则父类虚函数A被子类继承后被B函数覆盖。
  • 子类方法B此时也是一个虚函数

 编译错误

b.f()函数需要参数 ,虽然基类的f()函数不需要参数,但是被隐藏了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值