10、面向对象介绍

厨子大约 7 分钟C++C++基础编程程序厨

你肯定知道,C++是一门支持面向对象(Object-Oriented Programming,简称OOP)的语言,那什么是面向对象呢?

可以理解为面向对象是一种编程范式,它以“对象”为核心,将数据和操作数据的方法封装在一起,通过抽象、继承、多态等机制来组织代码。面向对象的主要目标是提高代码的可复用性、可维护性和可扩展性。

面向对象

面向对象三要素:继承、封装、多态。

核心概念

对象

对象是面向对象编程的基本单元,它是一个具体的实例,包含数据(属性)和行为(方法)。例如,在一个学生管理系统中,每个学生可以看作一个对象,具有姓名、年龄、学号等属性,以及选课、考试等方法。

类是对象的模板或蓝图,它定义了对象的属性和方法。类是抽象的,而对象是类的具体实例。例如,Student类可以定义学生的共同属性和行为,而具体的某个学生(张三)是Student类的一个对象。

封装

封装是将数据(属性)和操作数据的方法(行为)绑定在一起,并隐藏内部实现细节。通过访问修饰符(如publicprivateprotected),可以控制数据的访问权限,提高代码的安全性和可维护性。

继承

继承是一种机制,允许一个类(派生类)基于另一个类(基类)创建新的类。派生类继承了基类的属性和方法,并可以扩展或修改这些属性和方法。继承的主要目的是实现代码的复用

多态

多态是指同一个接口可以表现出不同的行为。通过多态,可以在运行时决定调用哪个类的具体实现。一般通过虚函数和抽象类来实现多态。

优势

  1. 模块化:将代码划分为独立的类和对象,便于管理和维护。
  2. 可复用性:通过继承和组合,可以复用已有的代码,减少重复劳动。
  3. 可扩展性:通过多态和抽象类,可以轻松扩展程序的功能。
  4. 可维护性:封装隐藏了内部实现细节,降低了代码的耦合性,方便后续修改和调试。

代码示例

#include <iostream>
#include <string>

// 定义一个类
class Student {
private:
    std::string name; // 私有属性
    int age;

public:
    // 构造函数
    Student(std::string n, int a) : name(n), age(a) {}

    // 公有方法
    void displayInfo() {
        std::cout << "Name: " << name << ", Age: " << age << std::endl;
    }

    // 设置年龄
    void setAge(int a) {
        if (a > 0) {
            age = a;
        } else {
            std::cout << "Invalid age!" << std::endl;
        }
    }

    // 获取年龄
    int getAge() {
        return age;
    }
};

int main() {
    // 创建对象
    Student student1("Alice", 20);
    Student student2("Bob", 22);

    // 调用对象的方法
    student1.displayInfo(); // 输出:Name: Alice, Age: 20
    student2.displayInfo(); // 输出:Name: Bob, Age: 22

    // 修改对象的属性
    student1.setAge(21);
    std::cout << "Alice's new age: " << student1.getAge() << std::endl; // 输出:Alice's new age: 21

    return 0;
}

类的继承与派生

继承的基本概念

继承是面向对象编程的重要特性之一,它允许一个类(派生类)基于另一个类(基类)来创建新的类。派生类继承了基类的属性和方法,同时可以扩展或修改这些属性和方法。

  • 基类(父类):被继承的类。
  • 派生类(子类):继承基类的类。

继承的语法

继承的语法如下:

class 派生类名 : 访问修饰符 基类名 {
    // 派生类的成员
};

访问修饰符可以是publicprotectedprivate,决定了基类成员在派生类中的访问权限。

继承的类型

  • 公有继承(public):基类的public成员在派生类中仍然是publicprotected成员仍然是protected
  • 保护继承(protected):基类的publicprotected成员在派生类中都变为protected
  • 私有继承(private):基类的publicprotected成员在派生类中都变为private

代码示例

#include <iostream>

// 基类
class Animal {
public:
    void eat() {
        std::cout << "Animal is eating." << std::endl;
    }
protected:
    void sleep() {
        std::cout << "Animal is sleeping." << std::endl;
    }
};

// 派生类
class Dog : public Animal {
public:
    void bark() {
        std::cout << "Dog is barking." << std::endl;
        sleep(); // 可以访问基类的protected成员
    }
};

int main() {
    Dog dog;
    dog.eat();  // 调用基类的public成员函数
    dog.bark(); // 调用派生类的成员函数
    // dog.sleep(); // 错误:sleep是protected成员,不能在类外访问
    return 0;
}

多态

多态的概念

多态是指同一个接口可以表现出不同的行为。在C++中,多态主要通过虚函数和抽象类来实现。

虚函数

虚函数是在基类中使用virtual关键字声明的函数,允许在派生类中重写该函数。通过基类指针或引用调用虚函数时,实际调用的是派生类的函数。

#include <iostream>

class Animal {
public:
    virtual void makeSound() {
        std::cout << "Animal makes a sound." << std::endl;
    }
};

class Dog : public Animal {
public:
    void makeSound() override {
        std::cout << "Dog barks." << std::endl;
    }
};

class Cat : public Animal {
public:
    void makeSound() override {
        std::cout << "Cat meows." << std::endl;
    }
};

int main() {
    Animal* animal1 = new Dog();
    Animal* animal2 = new Cat();

    animal1->makeSound(); // 输出:Dog barks.
    animal2->makeSound(); // 输出:Cat meows.

    delete animal1;
    delete animal2;
    return 0;
}

抽象类

抽象类是不能实例化的类,通常用于定义接口。抽象类中至少包含一个纯虚函数(**= 0**声明)。

#include <iostream>

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

class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing a circle." << std::endl;
    }
};

class Square : public Shape {
public:
    void draw() override {
        std::cout << "Drawing a square." << std::endl;
    }
};

int main() {
    Shape* shape1 = new Circle();
    Shape* shape2 = new Square();

    shape1->draw(); // 输出:Drawing a circle.
    shape2->draw(); // 输出:Drawing a square.

    delete shape1;
    delete shape2;
    return 0;
}

静态成员与友元函数

静态成员

静态成员属于类本身,而不是类的某个对象。静态成员变量在类的所有对象之间共享,静态成员函数只能访问静态成员变量。

#include <iostream>

class Counter {
public:
    static int count; // 静态成员变量
    Counter() {
        count++;
    }
    static void showCount() { // 静态成员函数
        std::cout << "Count: " << count << std::endl;
    }
};

int Counter::count = 0; // 初始化静态成员变量

int main() {
    Counter c1, c2, c3;
    Counter::showCount(); // 输出:Count: 3
    return 0;
}

友元函数

友元函数可以访问类的私有成员和保护成员。友元函数不是类的成员函数,但需要在类中声明。

#include <iostream>

class Box {
private:
    int width;
public:
    Box(int w) : width(w) {}
    friend void printWidth(Box box); // 声明友元函数
};

void printWidth(Box box) {
    std::cout << "Width: " << box.width << std::endl; // 访问私有成员
}

int main() {
    Box box(10);
    printWidth(box); // 输出:Width: 10
    return 0;
}

this指针与成员函数重载

this指针

this指针是一个隐含的指针,指向当前对象的地址。它用于在类的成员函数中访问当前对象的成员。

#include <iostream>

class MyClass {
private:
    int value;
public:
    void setValue(int value) {
        this->value = value; // 使用this指针区分成员变量和参数
    }
    void printValue() {
        std::cout << "Value: " << this->value << std::endl;
    }
};

int main() {
    MyClass obj;
    obj.setValue(42);
    obj.printValue(); // 输出:Value: 42
    return 0;
}

成员函数重载

成员函数重载是指在同一个类中定义多个同名函数,但参数列表不同。

#include <iostream>

class Math {
public:
    int add(int a, int b) {
        return a + b;
    }
    double add(double a, double b) {
        return a + b;
    }
};

int main() {
    Math math;
    std::cout << math.add(2, 3) << std::endl;       // 输出:5
    std::cout << math.add(2.5, 3.5) << std::endl;   // 输出:6
    return 0;
}

练习

  1. 设计一个基类Vehicle,包含虚函数start()stop()。派生出两个类CarBike,分别重写start()stop()函数。
  2. 实现一个类Calculator,包含静态成员函数addsubtractmultiplydivide,分别用于执行加、减、乘、除运算。
  3. 设计一个类Student,包含私有成员nameage,并提供一个友元函数printStudent,用于打印学生的信息。