C++笔试面试常见题(一)

面向对象编程

面向对象编程(OOP)最主要有以下四个性质:

  • 封装
  • 继承
  • 多态
  • 抽象

封装

我认为封装性是面向对象编程最让人印象深刻的一个性质。所谓面向对象编程,这里所说的对象(object)可以看做是一个个变量、一个个函数(方法),封装就是把对象的属性和方法“打包”在一起,封装成一个类。在类中,成员变量一般会被隐藏在类内部,只允许通过类的公有方法访问。这种方式保护了数据的完整性,防止外部代码直接修改对象的状态。同时,封装也有助于提高代码的模块化和可维护性。
对象封装在类里后,对于类而言每个对象就是类的成员,分为成员变量和成员函数。对于类,需要通过一些标记来区分哪些成员是自己私有的,哪些成员可以公开,这就需要用到访问控制修饰符

  1. private:私有成员
    1. 声明为private的成员只能在类的内部(即在类的成员函数中)访问和修改,类的外部和子类都无法直接访问private成员。
    2. 用途:通常用来保护类的内部数据不被外部代码或子类随意修改,从而确保类的封装性。
  2. public:共有成员
    1. 声明为public的成员可以被类的外部、子类和其他代码直接访问和修改。public成员通常用作类对外提供的接口。
    2. 用途:通常用来定义类的接口,使外部可以访问类的功能。
  3. protected:受保护成员
    1. 声明为protected的成员只能在类的内部和派生类(子类)中访问,外部代码不能直接访问。这种修饰符常用于继承中,使得子类可以访问父类的某些成员,但这些成员仍对外部代码隐藏。
    2. 用途:通常用于那些希望在继承结构中共享的成员,但又不希望直接暴露给外部的场景。
      需要注意的是,在class中,如果不指定访问控制修饰符,类成员的默认访问权限是private
      而在struct中,如果不指定访问控制修饰符,成员的默认访问权限是publicstruct是C和C++都有的一种结构体类型,在C语言中,struct主要用于将多个相关的数据组合成一个单独的复合类型,而在C++中,struct不仅可以组合数据,还可以包含函数,就像一个简单的类(class)一样。

继承

继承允许一个类从另一个类派生,继承其属性和方法。这使得代码具有更好的复用性,派生类可以扩展和修改基类的功能。C++支持单继承和多继承(一个类可以有多个基类)。

#include <iostream>
using namespace std;

class Animals
{
public:
    void show()
    {
        cout << "Animals show" << endl;
    }
};

class Pet
{
public:
    void show()
    {
        cout << "Pet show" << endl;
    }
    void play()
    {
        cout << "Pet playing" << endl;
    }
};

// 继承不加public默认是private继承
class Cat : public Animals // 单继承
{
public:
    void sound()
    {
        cout << "Cat sound" << endl;
    }
};

class Dog : public Animals, public Pet // 多继承
{
public:
    void bark()
    {
        cout << "Dog bark" << endl;
    }
};

int main(int argc, char const *argv[])
{
    Cat cat;
    cat.show();
    cat.sound();
    Dog dog;
    dog.bark();
    dog.play();
    // dog.show(); // 直接这样写会报错,二义性
    // 多继承二义性解决方法:
    dog.Animals::show();
    dog.Pet::show();
    return 0;
}

多态

多态允许相同的函数名或操作符在不同对象上表现出不同的行为。C++中的多态主要分为两种:编译时多态运行时多态

  • 编译时多态:通过函数重载和运算符重载来实现。
  • 运行时多态:通过虚函数实现。运行时确定调用成员函数的时候,会根据调用方法的对象的类型来执行不同的函数。
#include <iostream>
using namespace std;

class Animals
{
public:
    // 运行时多态
    virtual void makesound()
    {
        cout << "Animals sound" << endl;
    }

    // 编译时多态,重载
    void makesound(int num)
    {
        cout << num << " " << "Animals sound" << endl;
    }
};

class Dog : public Animals
{
public:
    void makesound() override
    {
        cout << "Dog sound" << endl;
    }

    // 编译时多态,重载
    void makesound(int num)
    {
        cout << num << " " << "Dog sound" << endl;
    }
};

int main(int argc, char const *argv[])
{
    Dog dog1;
    dog1.makesound();
    dog1.makesound(1);
    Dog *dog2 = new Dog;
    dog2->makesound();
    dog2->makesound(2);
    Dog *dog3 = static_cast<Dog *>(new Animals); // 下行转换,不安全
    dog3->makesound();
    Animals *dog4 = new Dog;  // 上行转换
    dog4->makesound();  // 运行时多态,调用的是派生类版本 输出:Dog sound
    dog4->makesound(4); // 调用的是基类重载版本 输出:4 Animals sound

    return 0;
}

在这段代码中,dog4的输出是比较复杂的。

  • 当调用dog4->makesound();时,makesound是一个虚函数。通过虚函数表(vtable),这个调用会在运行时根据dog4实际指向的对象类型(即Dog对象)来决定调用哪个版本的makesound,因此调用的是Dog类的版本,输出"Dog sound"。这就是运行时多态,又称动态绑定。
  • 当调用dog4->makesound(4);时,由于makesound(int)是一个重载的非虚函数,C++在编译期间就决定了要调用哪个版本的makesound。编译器会选择匹配Animals类中定义的makesound(int),因为dog4的类型是Animals*。这种行为称为编译时多态,又称静态绑定

抽象

抽象是指隐藏实现细节,只向外界暴露接口。C++通过抽象类(包含纯虚函数的类)实现抽象。抽象类不能被实例化,它们通常用作基类,定义子类必须实现的接口= 0用于定义纯虚函数,让基类成为抽象类

#include <iostream>
using namespace std;

class Shape
{
public:
    // 定义纯虚函数,该类为抽象类
    virtual void draw() = 0;
};

class Circle : public Shape
{
public:
	// 子类必须实现纯虚函数接口
    void draw() override
    {
        cout << "Drawing a circle" << endl;
    }
};

int main(int argc, char const *argv[])
{
    // Shape shape; // 错误,无法实例化抽象类
    Circle circle;
    circle.draw();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值