C++面试总结---封装、继承、多态

1.概念

  C++是一种面向对象的编程语言,支持封装、继承和多态的特性。下面分别介绍这几个概念及其在C++中的实现方式:

  1. 封装(Encapsulation):封装是一种将数据和操作数据的方法(函数)组合在一起的机制。它的目的是将数据和内部实现细节隐藏起来,只暴露必要的接口给外部使用。在C++中,可以使用类来实现封装。类可以将数据成员和成员函数封装在一起,并使用访问修饰符(public、private、protected)来控制访问权限。
class MyClass {
private:
    int data; // 私有数据成员,外部无法直接访问

public:
    void setData(int value) { data = value; } // 公有成员函数
    int getData() { return data; }
};
  1. 继承(Inheritance):继承是一种将已有类的属性和方法引入到新的类中的机制。被继承的类称为基类(或父类),继承的类称为派生类(或子类)。派生类可以访问基类的公有成员和受保护成员,但不能访问基类的私有成员。在C++中,可以使用关键字publicprivateprotected来设置继承的访问权限,默认为private
class BaseClass {
public:
    void baseMethod() { cout << "Base method" << endl; }
};

class DerivedClass : public BaseClass { // 公有继承
public:
    void derivedMethod() { cout << "Derived method" << endl; }
};
  1. 多态(Polymorphism):多态是一种同一操作具有多个不同实现方式的能力。C++中的多态通过虚函数(virtual function)和动态绑定(dynamic binding)来实现。虚函数是在基类中声明为虚函数的成员函数,在派生类中可以进行重写(override)。通过使用指向基类对象的指针或引用调用虚函数时,调用的是实际指向的派生类对象的虚函数。
class Shape {
public:
    virtual void draw() { cout << "Drawing shape" << endl; } // 虚函数
};

class Circle : public Shape {
public:
    void draw() override { cout << "Drawing circle" << endl; } // 重写虚函数
};

class Rectangle : public Shape {
public:
    void draw() override { cout << "Drawing rectangle" << endl; } // 重写虚函数
};

int main() {
    Shape* shape;
    Circle circle;
    Rectangle rectangle;

    shape = &circle; // 指向派生类对象的基类指针
    shape->draw(); // 调用派生类的虚函数

    shape = &rectangle;
    shape->draw();

    return 0;
}

  以上就是C++中封装、继承和多态的基本概念及其在C++中的实现方式。这些特性可以帮助开发者更好地组织和管理代码,提高代码的重用性和可扩展性。

2.多态的内部实现

  C++ 多态的内部实现是通过虚函数和虚指针来实现的。虚函数是在基类中声明的函数,通过在函数声明前面加上关键字 “virtual”,可以使该函数成为虚函数。派生类可以重写基类的虚函数,使用自己的实现。每个对象中都会有一个虚指针表(vtable),该表中存储了类的虚函数的地址。当一个类包含虚函数时,编译器会在对象的布局中插入一个虚指针,指向该类的虚函数表。通过虚指针和虚函数表,编译器可以在运行时确定调用的是哪个函数。当通过基类的指针或引用调用虚函数时,会根据运行时对象的实际类型来调用相应的函数,而不是根据指针或引用的静态类型来调用函数。
具体的调用过程如下:

  1. 当通过基类的指针或引用调用虚函数时,会根据指针或引用的静态类型找到对应的虚函数表。
  2. 根据虚指针在虚函数表中找到对应的虚函数地址。
  3. 调用找到的虚函数。
C++中的多态是通过虚函数和指针/引用来实现的。下面是一个简单的示例来说明多态的内部实现:

```cpp
#include <iostream>

// 基类 Shape
class Shape {
public:
    // 虚函数 area
    virtual float area() {
        return 0;
    }
};

// 派生类 Circle
class Circle : public Shape {
private:
    float radius;
public:
    Circle(float r) {
        radius = r;
    }
    // 重写基类的虚函数 area
    float area() {
        return 3.14*radius*radius;
    }
};

// 派生类 Rectangle
class Rectangle : public Shape {
private:
    float width;
    float height;
public:
    Rectangle(float w, float h) {
        width = w;
        height = h;
    }
    // 重写基类的虚函数 area
    float area() {
        return width*height;
    }
};

int main() {
    Shape* shape1 = new Circle(5);      // 使用基类指针指向派生类对象
    Shape* shape2 = new Rectangle(4, 6); // 使用基类指针指向派生类对象

    std::cout << "Circle area = " << shape1->area() << std::endl;
    std::cout << "Rectangle area = " << shape2->area() << std::endl;

    delete shape1;
    delete shape2;

    return 0;
}

  在这个示例中,定义了一个基类 Shape 和两个派生类 CircleRectangle,它们都重写了基类的虚函数 area
main 函数中,通过定义基类指针 shape1shape2,分别指向派生类对象 CircleRectangle,然后通过这些指针调用虚函数 area。由于虚函数的动态绑定特性,程序会根据指针所指向的对象的类型来调用相应的重写函数。

输出结果为:

Circle area = 78.5
Rectangle area = 24

  这里的实现原理是,在基类中声明虚函数 virtual float area(),告诉编译器在运行时对该函数进行动态绑定。基类的指针在运行时会根据实际指向的派生类对象来调用相应的重写函数。这就实现了多态的效果。

3.访问控制修饰符

  在C++中,public、private和protected是用于类的访问控制权限的关键字。

  1. public:public成员在类的内部和外部都是可访问的。这意味着可以在类的任何位置访问该成员,以及通过创建类的对象访问该成员。
  2. private:private成员只能在类的内部访问,无法通过类的对象直接访问该成员。这意味着其他非成员函数和其他类的成员函数都无法直接访问private成员。通常,应该将数据成员声明为private,以便通过公共的成员函数来访问和修改这些成员。
  3. protected:protected成员可以在类的内部访问,同时也可以在其派生类中访问。这意味着其派生类中的成员函数可以访问派生类所继承的protected成员。
    总结:
  • public成员可以在类的内部和外部访问;
  • private成员只能在类的内部访问;
  • protected成员可以在类的内部和派生类中访问。
1.三者继承后属性关系的变化
/public继承protected继承private继承
基类Publicpublicprotected不可见
基类Protectedprotectedprotected不可见
基类Privateprivateprotected不可见
#include<iostream>
using namespace std;
//
class A       //父类
{
private:
    int privatedateA;
protected:
    int protecteddateA;
public:
    int publicdateA;
};
//
class B :public A      //基类A的派生类B(继承)
{
public:
    void funct()
    {
        int b;
        b=privatedateA;   //error:基类中私有成员在派生类中是不可见的
        b=protecteddateA; //ok:基类的保护成员在派生类中为保护成员
        b=publicdateA;    //ok:基类的公共成员在派生类中为公共成员
    }
};
//
class C :private A  //基类A的派生类C(私有继承)
{
public:
    void funct()
    {
        int c;
        c=privatedateA;    //error:基类中私有成员在派生类中是不可见的
        c=protecteddateA;  //ok:基类的保护成员在派生类中为私有成员
        c=publicdateA;     //ok:基类的公共成员在派生类中为私有成员
    }
};
//
class D :protected A   //基类A的派生类D(保护继承)
{
public:
    void funct()
    {
        int d;
        d=privatedateA;   //error:基类中私有成员在派生类中是不可见的
        d=protecteddateA; //ok:基类的保护成员在派生类中为保护成员
        d=publicdateA;    //ok:基类的公共成员在派生类中为保护成员
    }
};
//
int main()
{
    int a; 
 
    B objB;
    a=objB.privatedateA;   //error:基类中私有成员在派生类中是不可见的,对对象不可见
    a=objB.protecteddateA; //error:基类的保护成员在派生类中为保护成员,对对象不可见
    a=objB.publicdateA;    //ok:基类的公共成员在派生类中为公共成员,对对象可见
 
    C objC;
    a=objC.privatedateA;   //error:基类中私有成员在派生类中是不可见的,对对象不可见
    a=objC.protecteddateA; //error:基类的保护成员在派生类中为私有成员,对对象不可见
    a=objC.publicdateA;    //error:基类的公共成员在派生类中为私有成员,对对象不可见
 
    D objD;
    a=objD.privatedateA;   //error:基类中私有成员在派生类中是不可见的,对对象不可见
    a=objD.protecteddateA; //error:基类的保护成员在派生类中为保护成员,对对象不可见
    a=objD.publicdateA;    //error:基类的公共成员在派生类中为保护成员,对对象不可见
 
    return 0;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Y_Mathison

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值