面向对象编程(Object-Oriented Programming, OOP)是一种程序设计范式,它将现实世界中的每个事物抽象为对象,在代码中用变量表示对象的特征,用函数表示对象的行为。具有相同特征和行为的一组对象还被抽象为类,它们的变量和函数被封装在类中。换句话说,类的成员包括变量(也称为类的属性)和函数(也称为类的方法)。此外,OOP利用类的继承和多态等机制来实现代码的复用和扩展。
类和对象的通俗解释:
类和对象的专业解释:
C++中类的基本组成包括类名,访问修饰符,成员变量(属性),成员函数(方法)。其中成员函数包括构造函数和析构函数。
类名用于标识类的名称,是类的唯一标识符。例如,定义了一个名为Person的类:
class Person{};
访问修饰符用于控制类成员(变量或函数)的访问权限,包括:
class Person{public: int publicVar; protected: int protectedVar;private: int privateVar;};
成员变量是类中的变量,用于存储与类相关的数据。成员变量可以是任何有效的C++数据类型,包括基本数据类型、数组、指针、引用、其他类类型等。
例如:
#include <iostream>#include <string>class Person {private: std::string name; // 私有成员变量 int age; // 私有成员变量public:};
成员函数是类中定义的函数,它可以访问类的成员变量,也可以使用其他成员函数。
返回类型 函数名(参数){函数体} // 参数可有可无
不过,当一个成员函数被声明为const时,它不能修改类的任何成员变量(除非这个成员变量被声明为mutable):
返回类型 函数名(参数) const {函数体}
构造函数和析构函数是两个特殊的成员函数:
构造函数:用于初始化类的对象。当创建类的对象时,构造函数会自动被调用。语法:
类名(参数){函数体} // 函数名必须与类名完全相同,并且没有返回类型,参数可有可无
可以用初始化列表语法来初始化属性值:
类名():属性1(值1),属性2(值2)...{函数体}
析构函数:用于在对象销毁前执行清理操作,如释放内存。当对象的生命周期结束时,析构函数会自动被调用。语法:
~类名(){函数体}
示例:
#include <iostream>#include <string>class Person {private: std::string name; // 私有成员变量 int age; // 私有成员变量public: // 构造函数 Person(std::string name_value, int age_value) : name(name_value), age(age_value) { std::cout << "Create Person with name: " << name << ", age: " << age << std::endl; } // 析构函数 ~Person() { std::cout << "Destroy Person with name: " << name << ", age: " << age << std::endl; } // 公有成员函数:获取名字 std::string getName() const { // 不被允许修改成员变量 return name; } // 公有成员函数:获取年龄 int getAge() const { // 不被允许修改成员变量 return age; } // 公有成员函数:设置年龄 void setAge(int age_value) { // 可以修改成员变量 age = age_value; std::cout << "Age updated to: " << age << std::endl; } // 公有成员函数:显示Person信息 void displayInfo() const { // 不被允许修改成员变量 std::cout << "Name: " << name << ", Age: " << age << std::endl; }};int main() { // 创建Person对象,构造函数将被调用 Person* person1 = new Person("SanZhang", 30); // 调用成员函数 person1->displayInfo(); person1->setAge(31); person1->displayInfo(); // 创建另一个Person对象,构造函数将被调用 Person* person2 = new Person("SiLi", 25); person2->displayInfo(); delete person1; // 调用person1的析构函数将 delete person2; // 调用person2的析构函数将 return 0;}
Create Person with name: SanZhang, age: 30Name: SanZhang, Age: 30Age updated to: 31Name: SanZhang, Age: 31Create Person with name: SiLi, age: 25Name: SiLi, Age: 25Destroy Person with name: SanZhang, age: 31Destroy Person with name: SiLi, age: 25
封装通过将数据和对数据的操作(即属性和方法)组合到一个单元(类)中,并通过访问控制符(private、protected、public)来限制对这些数据和操作的访问来实现。封装隐藏了对象的内部实现细节,只暴露必要的接口,提高了代码的可维护性和安全性。
在成员函数的示例代码中,就体现了封装性:
继承允许创建一个新类(派生类/子类)来继承一个重用类(基类/父类)的属性和方法,并在其基础上扩展新的功能。继承的基本语法为:class 派生类: public 基类。
例如,我们可以创建一个新的类Student ,它继承自Person类。Student 继承Person类的属性和方法的同时可以添加一些新的属性和方法:
#include <iostream> #include <string> // 基类 Person class Person { private: std::string name; // 私有成员变量 int age; // 私有成员变量 public: // 构造函数 Person(std::string name_value, int age_value) : name(name_value), age(age_value) { std::cout << "Create Person with name: " << name << ", age: " << age << std::endl; } // 析构函数 ~Person() { std::cout << "Destroy Person with name: " << name << ", age: " << age << std::endl; } // 公有成员函数:获取名字 std::string getName() const { return name; } // 公有成员函数:获取年龄 int getAge() const { return age; } // 公有成员函数:设置年龄 void setAge(int age_value) { age = age_value; std::cout << "Age updated to: " << age << std::endl; } // 公有成员函数:显示Person信息 void displayInfo() const { std::cout << "Name: " << name << ", Age: " << age << std::endl; } }; // 派生类 Student class Student : public Person { private: std::string studentId; // 添加的属性 public: // 构造函数 可以使用用初始化列表来调用父类的构造函数 Student(std::string name_value, int age_value, std::string id) : Person(name_value, age_value), studentId(id) { std::cout << "Create Student with ID: " << studentId << std::endl; } // 析构函数 ~Student() { std::cout << "Destroy Student with ID: " << studentId << std::endl; } // 添加的方法 std::string getStudentId() const { return studentId; } // 添加的方法 void displayStudentInfo() const { std::cout << "Student Info: "; displayInfo(); // 调用基类的displayInfo() std::cout << "ID: " << studentId << std::endl; } }; int main() { Student* student = new Student("SanZhang", 20, "A12138"); std::cout << "name:" << student->getName() << std::endl; // 调用父类的方法 student->displayStudentInfo(); // 调用添加的方法 // 当删除一个子类对象时,首先会调用子类的析构函数,然后再自动调用父类的析构函数 delete student; return 0; }
Create Person with name: SanZhang, age: 20Create Student with ID: A12138name:SanZhangStudent Info: Name: SanZhang, Age: 20ID: A12138Destroy Student with ID: A12138Destroy Person with name: SanZhang, age: 20
多态性使得一个接口可以有多个实现,从而可以使用同一接口来调用不同的实际方法。C++实现多态的方式主要有两种:静态多态 (主要通过函数重载和模板来实现) 和动态多态。 在讨论面向对象编程多态性时,通常指的是动态多态。动态多态主要通过继承和虚函数来实现,其中虚函数是一种在基类中声明,并在派生类中可以重写的函数,通过在基类中使用virtual关键字声明。
实现动态多态的一个关键是子类对象可以被被赋予父类类型的引用, 其地址也可以赋予父类类型的指针。 这被称为向上转型或隐式类型转换。
示例:
#include <iostream>#include <string>// 抽象基类 Personclass Person {protected: std::string name; int age;public: Person(std::string name_value, int age_value) : name(name_value), age(age_value) { std::cout << "Create Person with name: " << name << ", age: " << age << std::endl; } virtual ~Person() { // 虚析构函数 std::cout << "Destroy Person with name: " << name << ", age: " << age << std::endl; } virtual void displayInfo() const { std::cout << "Name: " << name << ", Age: " << age << std::endl; }};// 派生类class Student : public Person {private: std::string studentId;public: Student(std::string name_value, int age_value, std::string id) : Person(name_value, age_value), studentId(id) { std::cout << "Create Student with ID: " << studentId << std::endl; } ~Student() { std::cout << "Destroy Student with ID: " << studentId << std::endl; } void displayInfo() const override { std::cout << "Student Name: " << name << ", Age: " << age <<", ID: " << studentId << std::endl; }};void displayInfo(Person* person) { person->displayInfo();}int main() { // 创建 Person 对象 Person* person = new Person("SiLi", 35); // 创建 Student 对象 Person* student = new Student("SanZhang", 20, "A12138"); //面向对象的多态性允许不同的对象以相同的接口方式进行操作,但能做出不同的响应 displayInfo(person); displayInfo(student); delete person; delete student; return 0;}
Create Person with name: SiLi, age: 35Create Person with name: SanZhang, age: 20Create Student with ID: A12138Name: SiLi, Age: 35Student Name: SanZhang, Age: 20, ID: A12138Destroy Person with name: SiLi, age: 35Destroy Student with ID: A12138Destroy Person with name: SanZhang, age: 20
注: 继承的时候通常要将基类的析构函数定义为虚析构函数virtual ~Person(),这确保了当通过基类指针删除派生类对象时,派生类的析构函数也会被调用。这有助于防止资源泄露和其他与析构相关的问题。
抽象指的是将现实世界中的实体或概念转化为程序中对象的过程。这个过程中,我们识别并提取出这些实体或概念的共性,然后在程序中创建抽象类来代表这些共性。抽象类不能被实例化,但可以作为其他类的基类,为其他类提供一组通用的接口。这些接口一般是没有具体实现的纯虚函数,需要由派生类提供实现。
纯虚函数是在基类中声明的没有函数体(即没有定义)的虚函数,通常使用虚函数 = 0声明。纯虚函数必须在任何派生类中被重写,否则派生类也将成为抽象类(即不能被实例化)。
示例:
#include <iostream>#include <string>// 抽象基类 Personclass Person {protected: std::string name; int age;public: Person(std::string name_value, int age_value) : name(name_value), age(age_value) { std::cout << "Person created with name: " << name << ", age: " << age << std::endl; } virtual ~Person() { // 虚析构函数 std::cout << "Person destroyed with name: " << name << ", age: " << age << std::endl; } // 纯虚函数 virtual void displayInfo() const = 0;};// 派生类class Student : public Person {private: std::string studentId;public: Student(std::string name_value, int age_value, std::string id) : Person(name_value, age_value), studentId(id) { std::cout << "Create Student with ID: " << studentId << std::endl; } ~Student() { std::cout << "Destroy Student destroyed with ID: " << studentId << std::endl; } void displayInfo() const override { std::cout << "Student Name: " << name << ", Age: " << age <<", ID: " << studentId << std::endl; }};// 另一个派生类class Teacher : public Person {private: std::string subject;public: Teacher(std::string name_value, int age_value, std::string subj) : Person(name_value, age_value), subject(subj) { std::cout << "Create Teacher for subject: " << subject << std::endl; } ~Teacher() { std::cout << "Destroy Teacher for subject: " << subject << std::endl; } void displayInfo() const override { std::cout << "Teacher Name: " << name << ", Age: " << age <<", Subject: "<< subject << std::endl; }};void displayInfo(Person* person) { person->displayInfo();}int main() { // `Person`类是一个抽象类,因此不能直接创建`Person`类的对象 // Person person("SiLi", 35); // 报错 // 创建 Student 对象 Person* student = new Student("SanZhang", 20, "A12138"); Person* teacher = new Teacher("SiLi", 35, "C++ Programming"); displayInfo(student); displayInfo(teacher); delete student; delete teacher; return 0;}
Person created with name: SanZhang, age: 20Create Student with ID: A12138Person created with name: SiLi, age: 35Create Teacher for subject: C++ ProgrammingStudent Name: SanZhang, Age: 20, ID: A12138Teacher Name: SiLi, Age: 35, Subject: C++ ProgrammingDestroy Student destroyed with ID: A12138Person destroyed with name: SanZhang, age: 20Destroy Teacher for subject: C++ ProgrammingPerson destroyed with name: SiLi, age: 35