C++基础之面向对象高级

发布于:2025-06-21 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、继承 inherit

1.1 基本概念

继承是在一个已经存在的类的基础上新建一个类,新建的类拥有已经存在的类的特性。主要体现代码复用思想
● 已经存在被称为“基类(Base Class)”“父类”
● 新建被称为“派生类”“子类(Sub Class)”
基类和派生类是相对的,一个类即可能是基类又可能是派生类,取决于对比的类。

1.2 函数隐藏

通常派生类和基类都有做出一些差异化,其中函数隐藏就是一种修改基类函数的方式,另外也可以通过增加内容等方法做出差异化。

#include <iostream>

using namespace std;

class Father
{
private:
    string name="张";
public:
    

    
    string get_name()
    {
        return name;
    }
    void work()
    {
        cout<<"我是一个老师"<<endl;
    }

};

class Son:public Father
{

public:
//注意:基类的private成员子类可以继承,但是能否对其进行访问取决于基类是否开放了接口
//    void set_name(string name)
//    {
//        this->name=name;
//    }
    //若子类不满意父类的某个成员函数,可进行函数隐藏(保持函数签名相同以覆盖)
    void work()
    {
       cout<<"我是一个程序员"<<endl;
    }

};

int main()
{
   Son s;
   cout<<s.get_name()<<endl;
   s.work();

    return 0;
}

1.3 构造函数

1.3.1 继承中构造函数的限制

C++规定,派生类无法继承父类的构造函数,但派生类任意一个构造函数必须直接间接调用基类任意一个构造函数

默认情况编译器每个增加一个无参构造函数同时派生类无参构造函数调用基类无参构造函数(因此1.2代码可以正常运行)。

如果基类中只有一个有参构造函数,则子类调用不到基类的无参构造函数,会报错

如果使用之前知识解决上面问题可以基类增加无参构造函数基类有参构造函数增加参数默认值

#include <iostream>

using namespace std;

class Father
{
private:
    string name;
public:

 //方法1-->增加无参构造函数
//    Father():name("张"){}
 //方法2-->给父类有参构造函数增加默认值
    Father(string name="张"):name(name){}



    string get_name()
    {
        return name;
    }
    void work()
    {
        cout<<"我是一个老师"<<endl;
    }

};

class Son:public Father
{

public:

    void work()
    {
       cout<<"我是一个程序员"<<endl;
    }

};

int main()
{
   Son s;
   cout<<s.get_name()<<endl;

    return 0;
}

1.3.2 透传构造

如果编译器默认调用不到基类构造函数可以手动派生类构造函数调用基类构造函数
#include <iostream>

using namespace std;

class Father
{
private:
    string name="张";
public:

   Father(string name):name(name){}

    string get_name()
    {
        return name;
    }
    void work()
    {
        cout<<"我是一个老师"<<endl;
    }

};

class Son:public Father
{

public:

    //透传
    Son():Father("张"){}

     //透传
    Son(string name):Father(name){}

    void work()
    {
       cout<<"我是一个程序员"<<endl;
    }

};

int main()
{
   Son s;
   cout<<s.get_name()<<endl; //张
   s.work();

   Son s2("王");
    cout<<s2.get_name()<<endl; //王

    return 0;
}

1.3.3 委托构造

#include <iostream>

using namespace std;

class Father
{
private:
    string name="张";
public:

   Father(string name):name(name){}

    string get_name()
    {
        return name;
    }

    void work()
    {
        cout<<"我是一个老师"<<endl;
    }

};

class Son:public Father
{

public:

    //委托
    Son():Son("王"){}

     //透传
    Son(string name):Father(name){}

    void work()
    {
       cout<<"我是一个程序员"<<endl;
    }

};

int main()
{
   Son s;
   cout<<s.get_name()<<endl; //王
   s.work();

   Son s2("王");
    cout<<s2.get_name()<<endl;//王

    return 0;
}

但是委托构造要避免闭环

1.3.4 继承构造

这是C++11的新特性,继承构造不是构造函数继承而是表现出类似于继承特性本质透传构造

#include <iostream>

using namespace std;

class Father
{
private:
    string name="张";
public:
   Father():name("张"){};
   Father(string name):name(name){}

    string get_name()
    {
        return name;
    }

    void work()
    {
        cout<<"我是一个老师"<<endl;
    }

};

class Son:public Father
{

public:

  using Father::Father;
//有了这句话以后,编译器会帮我们自动添加以下的代码
//    Son():Father(){}
//    Son(string name):Father(name){}

    void work()
    {
       cout<<"我是一个程序员"<<endl;
    }

};

int main()
{
   Son s;
   cout<<s.get_name()<<endl; //张
   s.work();

   Son s2("王");
    cout<<s2.get_name()<<endl;//王

    return 0;
}

 1.4 对象的创建与销毁

#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("Father静态成员变量");

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("Son静态成员变量");

int main()
{
  cout<<"主函数开始"<<endl;



    Son* s=new Son;
    cout<<"调用s的功能"<<endl;
    delete s;


  cout<<"主函数结束"<<endl;
    return 0;
}

 

规律:

1. 对象创建销毁流程相反(对称)

2. 静态成员变量生命周期程序整个运行周期

3. 类型代码内存开辟都是基类优先,销毁都是派生类优先

 1.5 多重继承

1.5.1 基本使用

C++支持继承一个派生类可以多个基类

多重继承派生类对于每个基类关系都可以看做一个单一继承

#include <iostream>

using namespace std;

class Sofa
{

public:
    void sit()
    {
        cout<<"坐在沙发上"<<endl;
    }

};

class Bed
{
public:
    void lay()
    {
        cout<<"躺在床上"<<endl;
    }

};

class Sofabed:public Sofa,public Bed
{
    
};

int main()
{
  
    Sofabed sb;
    sb.sit();
    sb.lay();
            
    
    return 0;
}

1.5.2 二义性 之 重名成员

在多重继承如果多个基类之间拥有相同名称成员通过对象调用这些重名成员出现二义性问题

解决方法:调用重名成员使用 基类名称:: 显式调用

#include <iostream>

using namespace std;

class Sofa
{

public:
    void sit()
    {
        cout<<"坐在沙发上"<<endl;
    }
    void position()
    {
         cout<<"放在客厅"<<endl;
    }

};

class Bed
{
public:
    void lay()
    {
        cout<<"躺在床上"<<endl;
    }
    void position()
    {
         cout<<"放在卧室"<<endl;
    }

};

class Sofabed:public Sofa,public Bed
{


};

int main()
{

    Sofabed sb;
    sb.sit();
    sb.lay();
    
//  sb.position(); 出现二义性错误!!
    
    //使用作用域限定符
    sb.Bed::position();
    sb.Sofa::position();
    
    return 0;
}

1.5.3 二义性 之 菱形继承

菱形继承也成为钻石继承指的是一个派生类多个基类多个基类又有一个共同基类
解决方法1:作用域限定符
#include <iostream>

using namespace std;
class Furniture
{
public:
    void func()
    {
        cout<<"我是家具"<<endl;
    }
    
};

class Sofa:public Furniture
{


};

class Bed:public Furniture
{


};

class Sofabed:public Sofa,public Bed
{


};

int main()
{

    Sofabed sb;
    sb.sit();
    sb.lay();

//    sb.func();
    //方法一
    sb.Bed::func();
    sb.Sofa::func();
   
    
    return 0;
}

解决方法2:虚继承 

 虚继承底层原理编译器相关SofaBed类使用virtual继承FurnitureSofa对象Bed对象都会一个隐藏指针指针指向Furniture一个——虚基类查询这个可以找到Furniture函数调用位置

真正虚继承定义SofaSofaBedBedSofaBed继承关系此时SofaBed对象就会拥有两个继承来的隐藏虚基类表指针SofaBed对象调用Furniture相关函数通过隐藏成员指针查询调用位置进行比对避免二义性问题

         

        

 

#include <iostream>

using namespace std;
class Furniture
{
public:
    void func()
    {
        cout<<"我是家具"<<endl;
    }
};

class Chair:public Furniture
{

};

class Sofa:virtual public Furniture
{


};
class Bed:virtual public Furniture
{

};
class Sofabed:public Sofa ,public Bed
{

};

int main()
{
    Sofabed sb;
    Sofa s;
    Bed b;
    Chair c;
    Furniture f;
    cout<<sizeof(f)<<endl; //1
    cout<<sizeof(s)<<" "<<sizeof(b)<<endl;//4  4
    cout<<sizeof(sb)<<endl;//8
    sb.func();//我是家具


    return 0;
}

1.6 权限

1.6.1 三种权限修饰符

  • private
  • protected
  • public

三种权限修饰符修饰成员访问区别如下 

在类内

派生类内

全局(例如主函数中)

private

×

×

protected

×

public

#include <iostream>

using namespace std;
class Father
{
private:
    string str1="私有成员";
protected:
    string str2="保护成员";
public:
    string str3="共有成员";
   void test()
    {
       cout<<str1<<endl;
       cout<<str2<<endl;
       cout<<str3<<endl;
    }
};

class Son:public Father
{
public:
   void test2()
    {
//        cout<<str1<<endl; 无法访问
        cout<<str2<<endl;
        cout<<str3<<endl;

    }
};

int main()
{

  Father f;
  f.test();
  Son s;
  s.test2();

//  cout<<f.str1<<endl; 无法访问
//  cout<<f.str2<<endl;  无法访问
  cout<<f.str3<<endl;


    return 0;
}

1.6.2 公有继承

公有继承下,基类的private成员可以被派生类继承,但是无法直接访问。基类的protected和public成员继承到派生类仍然作为派生类的protected和public的成员(权限不变)。

#include <iostream>

using namespace std;
class Father
{
private:
    string str1="私有成员";
protected:
    string str2="保护成员";
public:
    string str3="共有成员";
   void test()
    {
       cout<<str1<<endl;
       cout<<str2<<endl;
       cout<<str3<<endl;
    }
};

class Son:public Father
{
public:

//    str1继承了,但无法直接访问
//    str2继承了,并作为Son的protected成员-->类内和派生类内访问
//    str3继承了,并作为Son的public成员-->均可访问


};

class Grandson:public Son
{
public:
    Grandson()
    {
        cout<<str2<<endl;
        cout<<str3<<endl;
    }
};

int main()
{

  Son s;
  Grandson g;


//  cout<<s.str2<<endl;  无法访问
  cout<<s.str3<<endl;


    return 0;
}

1.6.3 保护继承

保护继承下,基类private成员可以派生类继承但是无法直接访问基类protectedpublic成员继承到派生类都变成派生类的protected成员。
#include <iostream>

using namespace std;
class Father
{
private:
    string str1="私有成员";
protected:
    string str2="保护成员";
public:
    string str3="共有成员";
   void test()
    {
       cout<<str1<<endl;
       cout<<str2<<endl;
       cout<<str3<<endl;
    }
};

class Son:protected Father
{
public:

//    str1继承了,但无法直接访问
//    str2继承了,并作为Son的protected成员-->类内和派生类内访问
//    str3继承了,并作为Son的protected成员-->类内和派生类内访问


};

class Grandson:public Son
{
public:
    Grandson()
    {
        cout<<str2<<endl;
        cout<<str3<<endl;
    }
};

int main()
{

  Son s;
  Grandson g;


//  cout<<s.str2<<endl;  无法访问
//  cout<<s.str3<<endl;  无法访问


    return 0;
}

1.6.4 私有继承

私有继承下,基类private成员可以派生类继承但是无法直接访问基类protectedpublic成员继承到派生类都变成派生类的private成员。
#include <iostream>

using namespace std;
class Father
{
private:
    string str1="私有成员";
protected:
    string str2="保护成员";
public:
    string str3="共有成员";

};

class Son:private Father
{
public:

//    str1继承了,但无法直接访问
//    str2继承了,并作为Son的private成员-->只能在Son类内访问
//    str3继承了,并作为Son的private成员-->只能在Son类内访问
    Son()
    {
         cout<<str2<<endl;
         cout<<str3<<endl;
    }

};

class Grandson:public Son
{
public:
    Grandson()
    {
        
       // cout<<str2<<endl; 无法访问
       // cout<<str3<<endl; 无法访问
    }
};

int main()
{

  Son s;
  Grandson g;


//  cout<<s.str2<<endl;  无法访问
//  cout<<s.str3<<endl;  无法访问


    return 0;
}

二、多态 polymorphism

2.1 函数覆盖 override

函数覆盖也被称为函数重写函数覆盖多态触发条件之一

函数覆盖函数隐藏相似最大区别基类被覆盖函数需要使用virtual修饰,被virtual修饰函数虚函数

虚函数以下性质

● 虚函数具有传递性基类虚函数可以派生类新覆盖函数变成虚函数

● 成员函数可以设置为虚函数,静态成员函数不能设置为虚函数。

● 构造函数不能设置为虚函数,析构函数可以设置为虚函数。

● 如果函数声明定义分离只需要使用virtual修饰声明即可
●  C++ 1 1 可以 使用 override 关键字 验证 是否 覆盖 成功


网站公告

今日签到

点亮在社区的每一天
去签到