C++多态知识点梳理
多态
多态的概念:
多态就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
比如构成多态的俩个父子类,我们调用同一个函数,可能会产生不同的行为,比如普通人买票全价,学生买票半价或者打八折。
class Person
{
public:
virtual void BuyTicke()
{
cout << "买票-全价" << endl;
}
};
class Student:public Person
{
public:
virtual void BuyTicke()
{
cout << "买票-半价" << endl;
}
};
void Func(Person& p)
{
p.BuyTicke();
}
void Func(Person* p)
{
p->BuyTicke();
}
int main()
{
Person p;
Person* ps = &p;
Func(ps);
Student s;
Func(&s);
return 0;
}
我们可以发现这个代码的神奇之处,按照我们之前的理解,这个代码p.BuyTicke();/p->BuyTicke();都是按照类型调用的。会调用Person类的函数。
这个就是多态的魅力,通过传入对象的不同,可以实现不同的行为。
.
多态的定义及实现
多态的构成条件
首先构成多态的要满足俩种条件:
1 被调用的函数必须是虚函数,且必须派生类的虚函数进行重写
2 必须是父类的指针或者引用;
什么是虚函数呢?
就是在普通的成员函数前面加上virtual关键字,就会变成虚函数(注意一定是成员函数)。普通函数是不行的。
虚函数的重写:虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。
虚函数重写的俩个例外:
- 派生类被调用的虚函数可以不加virtual
class Person
{
public:
virtual void BuyTicke()
{
cout << “买票-全价” << endl;
}
};
class Student:public Person
{
public:
void BuyTicke()
{
cout << “买票-半价” << endl;
}
};
2 派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指
针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变
class Person
{
public:
virtual Person& BuyTicke()
{
cout << "买票-全价" << endl;
return *this;
}
};
class Student :public Person
{
public:
Student& BuyTicke()
{
cout << "买票-半价" << endl;
return *this;
}
};
int main()
{
return 0;
}
面试常考题
析构函数是虚函数吗?为什么呢?
首先析构函数前面加上virtual关键字满足三同,因为它的名字被编译器给默认生成destory(),因此析构函数是虚函数。为什么它要变成虚函数呢?
class Person
{
public:
~Person()
{
cout << "~Person()" << endl;
}
};
class Student:public Person
{
public:
~Student()
{
cout << "~Student()" << endl;
}
};
int main()
{
Person p;
Student st;
return 0;
}
这边我们看不构成虚函数也是没问题的。
但是我们改一下代码,然后再看一下这种情况。
class Person
{
public:
~Person()
{
cout << "~Person()" << endl;
}
};
class Student:public Person
{
public:
~Student()
{
cout << "~Student()" << endl;
delete st;
}
string* st = new string;
};
int main()
{
Person* p = new Student;
delete p;
return 0;
}
这边就发生了内存泄漏的问题,原因是因为这个delete p;只调用了p->destory()+operator delete()释放空间,这个p是Person类型,普通对象调用看对象的类型,但是如何解决这个问题呢?让他变成多态不就行了吗?让他看指向的对象就调用哪个对象的析构函数。
class Person
{
public:
virtual ~Person()
{
cout << "~Person()" << endl;
}
};
class Student:public Person
{
public:
virtual~Student()
{
cout << "~Student()" << endl;
delete st;
}
string* st = new string;
};
int main()
{
Person* p = new Student;
delete p;
return 0;
}
final 和override关键字
final让派生类不能重写虚函数;
class A
{
public:
virtual void Func()final
{}
};
class B:public A
{
public:
virtual void Func()
{}
};
int main()
{
return 0;
}
override检查派生类的虚函数是否构成重写
class A
{
public:
virtual void Func()
{}
};
class B:public A
{
public:
virtual void Func()override
{}
};
int main()
{
return 0;
}
5 重载、覆盖(重写)、隐藏(重定义)的对比
重载:在一个作用域 函数名相同,参数类型不同;
覆盖(重写):虚函数+三同;
隐藏:基类和派生类中只要命名相同就会构成隐藏;