面向对象核心

一、继承

在基类基础上创立一个新的类,新类中继承了基类了内容,同时可以添加自己独有的内容,简化了程序

已经存在的类成为基类或父类;

从父类或基类基础上新建的类成为派生类或子类

#include <iostream>
using namespace std;
class Father{
public://公共部分
    string first_name="王";
    void work(){
        cout<<"厨师"<<endl;
    }
};
//说明Son类是Father类的派生类,继承了Father的内容
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 透传构造

派生类中的构造函数调用基类的构造函数

#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<<endl;
    }
};
class Son:public Father{
public:
    Son(string name):Father(name){}//对应一个参数的基类函数
    Son(string name,int age):Father(name,age){}//对应两个参数的基类函数
};
int main(){
    Son s("张",45);
    s.show();//张 45
    Son s1("王");
    s1.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){cout<<"子类一个参数"<<endl;}
    Son(string name,int age):Father(name,age){cout<<"子类两个参数"<<endl;}
};
int main(){
    Son s("张",23);
    s.show();//张 23
    cout<<endl;
    Son s1("王");
    s1.show();//王
}

运行结果:

Father 两个参数构造函数
子类两个参数
张 23

Father 两个参数构造函数    (首先创建father类的构造函数)
子类两个参数              (然后创建两个参数的子类构造函数) 
子类一个参数              (创建一个参数的子类构造函数)
王 18                    (显示show)

对象的创建和销毁

#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;   //s_value的类内声明
    Value value=Value("Father类的成员变量");
    Father(){//构造函数
        cout<<"Father的构造函数"<<endl;
    }
    ~Father(){//析构函数
        cout<<"Father的析构函数"<<endl;
    }
};
Value Father::s_value=Value("父类的静态Value");  //s_value的类外定义

class Son:public Father{
public:
    static Value s_value;     //s_value的类内声明
    Value value=Value("Son类的成员变量");
    Son(){
        cout<<"Son的构造函数"<<endl;
    }
    ~Son(){
        cout<<"Son的析构函数"<<endl;
    }
};
Value Son::s_value=Value("子类的静态Value");  //s_value的类外定义

int main()
{
    cout<<"main方法开始"<<endl;
    {
        Son s;
        cout<<"Son类对象使用中。。。"<<endl;
    }
    cout<<"main方法结束"<<endl;
}

运行结果:

父类的静态Value创建了             
子类的静态Value创建了
main方法开始                     //进入主函数
Father类的成员变量创建了
Father的构造函数
Son类的成员变量创建了
Son的构造函数
Son类对象使用中。。。             //son类对象使用中
Son的析构函数
Son类的成员变量销毁了
Father的析构函数
Father类的成员变量销毁了
main方法结束                    //退出主函数
子类的静态Value销毁了
父类的静态Value销毁了

特点:

创建过程和销毁过程是对称的
同样的内容,父类先创建,然后派生类。销毁的时候先派生类 后父类
静态成员最先创建,最后销毁
继承 提高开发效率 但是会稍微降低运行效率

1、3 多重继承

一个派生类它有多个基类,会从多个基类中继承方法

#include <iostream>
using namespace std;
class Car{//车
public:
    void driver(){
        cout<<"可以驾驶"<<endl;
    }
};
class House{//房子
public:
    void sleep(){
        cout<<"可以睡觉"<<endl;
    }
};
class Carhouse:public Car,public House{
};

int main()
{
    Carhouse s;//房车
    s.driver();
    s.sleep();
}

多重继承的二义性问题

当多个基类中有同名的成员。此时就会产生二义性

可以通过类名::方法来区分

#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 (公共)本类中可以访问,在子类中可以访问,在全局中也可以访问

不管哪种继承方式,基类中私有的成员属性。都是不能直接访问

2、1 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;//public    可以访问
        cout<<str2<<endl;//protected 可以访问
        //cout<<str3<<endl;//父类私有不能访问
    }

};
int main()
{
    Son s;
    s.show();
}

2、2 protected继承:

protected继承之后。所有继承过来的属性都会变成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;//protected 可以访问
        cout<<str2<<endl;//protected 可以访问
        //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类型,在全局中不能访问
}

2、3 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、 多态

多态就是多种状态,可以一个接口实现多种状态。根据传入对象的不同,虽然调用同样接口,但选择不同的代码实现。

多态需要三种条件:

1. pubic继承

2. 派生类覆盖基类中的虚函数

3. 接口参数为基类对象的指针或者基类引,指向派生对象

虚函数:

virtual关键字修饰的函数就是虚函数

1. 当函数覆盖成功后,虚函数具有传递性。默认的函数都有virtual关键字

2. 成员函数和析构函数可以定义为虚函数。静态函数和构造函数不可以

3. 如果声明和定义分离时,virtual关键字只需要加在声明处。

没有使用多态的情况

这时指针是哪个对象定义的, 就调用哪个对象类型中的方法

如下方Animal * ,就会执行Aniaml类中的方法

#include <iostream>
using namespace std;
class Animal{
public:
    void eat(){
        cout<<"吃东西"<<endl;
    }
};
class Dog:public Animal{
public:
    void eat(){ 
      cout<<"吃狗粮"<<endl;
    }
};
class House:public Animal{
public:
    void eat(){
        cout<<"吃草"<<endl;
    }
};
int main()
{
    Dog d;
    d.eat();//吃狗粮
    Animal *a=&d;
    a->eat();//吃东西
}

3、1多态的实现

基类中函数用virtual修饰 派生类中 覆盖的函数后面加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、2 多态应用

接口使用多态

#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 test(Animal * a){//指针
    a->eat();
}
void test2( Animal &b){//引用
    b.eat();
}
int main()
{
    //接口调用
    Cat c2;
    test2(c2);
    test1(&c2);
}

基类指针或者基类引用(隐含继承)调用虚函数的时候,就是多态。触发查虚函数表

3.3多态可能出现的问题:

#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的析构函数
}

错误原因:

析构函数没有进行覆盖。实际析构函数也没法覆盖。但是可以把析构函数定义为虚函数

如果一个类可以能成为基类

4.抽象类(掌握)

抽象的概念。只有一些说明,没有具体实现,相当于一个框架的作用。抽象类中不具体的创建对象,不能作为参数类和和返回值类型

一个类中有纯虚函数,这个类就是抽象类;

如果一个类是抽象类,那么一定会有纯虚函数

如果继承了抽象类,需要实现全部实现,才能创建对象。否则它也会成为抽象类

#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;

}
#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();
}
#include <iostream>
using namespace std;
class Shape{
public:
    int a=10;
    int b=5;
    virtual void area()=0;
    virtual void perimeter()=0;
};
class Rectangle:public Shape{
public:
    void area(){
        cout<<"长方形面积:  "<<a*b<<endl;
    }
    void perimeter(){
        cout<<"长方形周长:  "<<(a+b)*2<<endl;
    }
};

int main()
{
    Rectangle r;
    r.area();
    r.perimeter();
}

猜你喜欢

转载自blog.csdn.net/m0_74762280/article/details/129169867