面向对象核心
1.继承(重点)
从基类(父类)基础上建立一个新的类。继承一些方法,体现了代码复用的思想已经存在的类称为父类或基类
从父类或基类基础上新建的类称为派生类或子类
#include <iostream>
using namespace std;
class Father{
public:
string first_name="王";
void work(){
cout<<"理发师"<<endl;
}
};
class Son:public Father{
public:
void work(){
cout<<"程序员"<<endl;
}
void play(){
cout<<"玩游戏"<<endl;
}
};
int main()
{
Son s;
cout<<s.first_name<<endl;
s.work();
s.play();
}
上面派生类没有继承基类的带参的构造函数。因为如果不手动写构造函数
子类有一个默认无参的构造函数
父类有一个无参的构造函数
子类的无参构造函数中,会调用基类的无参构造函数
需要调用基类的代码来完成派生类中基类代码内存的开辟。 派生类的任意一个构造函数,都必须直接或者间接调用基类的任意一个构造函数
1.1透传构造
直接在派生类中的构造函数调用基类的构造函数
Son(string name) ———》Father(name)
Son(string name,int age)---》Father(name,age)
#include <iostream>
using namespace std;
class Father{
private:
string first_name;
int age;
public:
Father(string name){
this->first_name=name;
}
Father(string name,int age){
this->first_name=name;
this->age=age;
}
void show(){
cout<<first_name<<" "<<age;
}
};
class Son:public Father{
public:
Son(string name):Father(name){}
Son(string name,int age):Father(name,age){}
};
int main()
{
Son s("张三",18);
s.show();
}
1.2委托构造
某个类中的构造函数调用这个类中的重载的构造函数
#include <iostream>
using namespace std;
class Father{
private:
string first_name;
int age;
public:
Father(string name){
cout<<"Father一个参数构造函数"<<endl;
this->first_name=name;
}
Father(string name,int age){
cout<<"Father两个参数构造函数"<<endl;
this->first_name=name;
this->age=age;
}
void show(){
cout<<first_name<<" "<<age<<endl;
}
};
class Son:public Father{
public:
Son(string name):Son(name,18){}
Son(string name,int age):Father(name,age){}
};
int main()
{
Son s("张",20);
s.show();
Son s2("李");
s2.show();
}
1.3对象的创建和销毁
#include <iostream>
using namespace std;
class Value{
private:
string name;
public:
Value(string name):name(name){
cout<<name<<"创建了"<<endl;
}
~Value(){
cout<<name<<"销毁了"<<endl;
}
};
class Father{
public:
static Value s_value;
Value value=Value("Father类的成员变量");
Father(){
cout<<"Father的构造函数"<<endl;
}
~Father(){
cout<<"Father的析构函数"<<endl;
}
};
Value Father::s_value=Value("父类的静态Value");
class Son:public Father{
public:
static Value s_value;
Value value=Value("Son类的成员变量");
Son(){
cout<<"Son的构造函数"<<endl;
}
~Son(){
cout<<"Son的析构函数"<<endl;
}
};
Value Son::s_value=Value("子类的静态Value");
int main()
{
cout<<"main方法开始"<<endl;
{
Son s;
cout<<"Son类对象使用中。。。"<<endl;
}
cout<<"main方法结束"<<endl;
}
过程
特点:
创建过程和销毁过程是对称的(先创建的后销毁,类似栈先进后出)
同样的内容,父类先创建,然后派生类。销毁的时候先派生类 后父类
静态成员最先创建,最后销毁
继承 提高开发效率 但是会稍微降低运行效率
1.4多重继承
一个派生类它有多个基类,会从多个基类中继承方法
#include <iostream>
using namespace std;
class Sofa{
public:
void sit(){
cout<<"可以坐着";
}
};
class Bed{
public:
void lay(){
cout<<"可以躺着"<<endl;
}
};
class SofaBed:public Sofa,public Bed{
};
int main()
{
SofaBed s;
s.sit();
s.lay();
}
Car driver()
House sleep()nn
房车继承Car 和House
1.5多重继承的二义性问题
当多个基类中有同名的成员。此时就会产生二义性
可以通过类名::方法来区分
#include <iostream>
using namespace std;
class Sofa{
public:
void sit(){
cout<<"可以坐着"<<endl;
}
void position(){
cout<<"放在客厅";
}
};
class Bed{
public:
void lay(){
cout<<"可以躺着"<<endl;
}
void position(){
cout<<"放在卧室";
}
};
class SofaBed:public Sofa,public Bed{
};
int main()
{
SofaBed s;
s.sit();
s.lay();
s.Sofa::position();//沙发的位置
s.Bed::position();//床的位置
}
2.权限 (重要)
private 本类中可以访问 在子类中不可以访问 全局中不可以访问
protected 本类中可以访问 在子类中可以访问 全局中不可以访问
public 本类中可以访问 在子类中可以访问 全局中也可以访问
不同权限的继承
不管哪种继承方式,基类中私有的成员属性。都是不能直接访问
public继承
public继承之后 成员在原来基类是什么权限,在派生类中还是什么权限
#include <iostream>
using namespace std;
class Father{
public:
string str1="public";
protected:
string str2="protected";
private:
string str3="private";
};
class Son:public Father{
public:
void show(){
cout<<str1<<endl;
cout<<str2<<endl;
//cout<<str3<<endl;父类私有不能访问
}
};
int main()
{
Son s;
s.show();
}
protectd继承
protect继承之后。所有继承过来成员都会在Son中变成protected
#include <iostream>
using namespace std;
class Father{
public:
string str1="public";
protected:
string str2="protected";
private:
string str3="private";
};
class Son:protected Father{
public:
void show(){
cout<<str1<<endl;
cout<<str2<<endl;
//cout<<str3<<endl;父类私有不能访问
}
};
class GrandSon:public Son{
public:
void show(){
cout<<str1<<endl;
cout<<str2<<endl;
//cout<<str3<<endl;父类私有不能访问
}
};
int main()
{
Father f;
cout<<f.str1<<endl;
Son s;
cout<<s.str1<<endl; //在子类中变成了protected类型,在全局中不能访问
}
private继承
派生类私有继承之后 属性在派生中都变成私有.也就是下方的Son类中的属性都变成私有
#include <iostream>
using namespace std;
class Father{
public:
string str1="public";
protected:
string str2="protected";
private:
string str3="private";
};
class Son:private Father{
public:
void show(){
cout<<str1<<endl;
cout<<str2<<endl;
//cout<<str3<<endl;父类私有不能访问
}
};
class GrandSon:public Son{
public:
void show(){
// cout<<str1<<endl; //因为子类中变成私有 孙子类全访问不到
// cout<<str2<<endl;
}
};
int main()
{
Son s;
s.show();
GrandSon gs;
gs.show();
}
3.多态(重要)
函数重载是静态多态 编译的时候就已经确定
多态就是多种状态,可以一个接口实现多种状态。根据传入对象的不同,虽然调用同样接口,但 选择不同的代码实现。
多态需要三种条件:
- pubic继承
- 派生类覆盖基类中的虚函数
- 接口参数为基类对象的指针或者基类引,指向派生对象
virtual关键字修饰的函数就是虚函数
- 当函数覆盖成功后,虚函数具有传递性。默认的函数都有virtual关键字
- 成员函数和析构函数可以定义为虚函数。静态函数和构造函数不可以
- 如果声明和定义分离时,virtual关键字只需要加在声明处。
3.1没有使用多态的情况
这时指针是哪个对象定义的, 就调用哪个对象类型中的方法。如下方Animal * ,就会Aniaml类中的方法
#include <iostream>
using namespace std;
class Animal{
public:
void eat(){
cout<<"吃东西"<<endl;
}
};
class Dog:public Animal{
public:
void eat(){
cout<<"吃狗粮"<<endl;
}
};
class Cat:public Animal{
public:
void eat(){
cout<<"吃鱼"<<endl;
}
};
int main()
{
Dog d;
d.eat(); //吃狗粮
Animal * a=&d; //静态的
a->eat(); //吃东西
Cat c;
Animal * b=&c;
b->eat(); //吃东西
}
3.2多态的实现
覆盖的函数后面加override 关键字。如果没有发生重写就会报错
#include <iostream>
using namespace std;
class Animal{
public:
virtual void eat(){
cout<<"吃东西"<<endl;
}
};
class Dog:public Animal{
public:
void eat() override
{
cout<<"吃狗粮"<<endl;
}
};
class Cat:public Animal{
public:
void eat() override
{
cout<<"吃鱼"<<endl;
}
};
int main()
{
Dog d;
d.eat(); //吃狗粮
Animal * a=&d;
a->eat(); //吃东西
Cat c;
Animal * b=&c;
b->eat(); //吃东西
}
3.3多态应用
接口使用多态
#include <iostream>
using namespace std;
class Animal{
public:
virtual void eat(){
cout<<"吃东西"<<endl;
}
};
class Dog:public Animal{
public:
void eat(){
cout<<"吃狗粮"<<endl;
}
};
class Cat:public Animal{
public:
void eat(){
cout<<"吃鱼"<<endl;
}
};
void test1(Animal * a){
a->eat();
}
void test2( Animal &b){
b.eat();
}
int main()
{
//接口调用
Cat c2;
test2(c2);
test1(&c2);
}
基类指针或者基类引用(隐含继承)调用虚函数的时候,就是多态。触发查虚函数表
原理:
每一个有虚函数的类都有虚函数表
3.4多态可能出现的问题:
#include <iostream>
using namespace std;
class Animal{
public:
virtual void eat(){
cout<<"吃东西"<<endl;
}
~Animal(){
cout<<"Animal的析构函数"<<endl;
}
};
class Dog:public Animal{
public:
void eat(){
cout<<"吃狗粮"<<endl;
}
~Dog(){
cout<<"Dog类的析构函数"<<endl;
}
};
int main()
{
Animal * a=new Dog;
delete a; //只会调用Animal的析构函数
}
错误原因:
析构函数没有进行覆盖。实际析构函数也没法覆盖。但是可以把析构函数定义为虚函数
#include <iostream>
using namespace std;
class Animal{
public:
virtual void eat(){
cout<<"吃东西"<<endl;
}
virtual ~Animal(){
cout<<"Animal的析构函数"<<endl;
}
};
class Dog:public Animal{
public:
void eat(){
cout<<"吃狗粮"<<endl;
}
~Dog(){
cout<<"Dog类的析构函数"<<endl;
}
};
int main()
{
// Dog * d=new Dog;
// delete d;
Animal * a=new Dog;
delete a;
}
4.抽象类(掌握)
抽象的概念。只有一些说明,没有具体实现,相当于一个框架的作用。抽象类中不具体的创建对象,不能作为参数类和和返回值类型
一个类中有纯虚函数,这个类就是抽象类;
如果一个类是抽象类,那么一定会有纯虚函数
如果继承了抽象类,需要实现全部实现,才能创建对象。否则它也会成为抽象
#include <iostream>
using namespace std;
class Shape{
public:
virtual void area()=0;
virtual void perimeter()=0; //面积
};
class Rectangle:public Shape{
public:
void area(){
cout<<"长方形面积"<<endl;
}
void perimeter(){
cout<<"长方形的周长"<<endl;
}
};
int main()
{
Rectangle r;
r.area();
r.perimeter();
}