C++学习之面向对象

简介: C++学习之面向对象

面向对象的概念

面向对象(Object-Oriented)是一种软件开发的编程范式或思想方式。在面向对象编程(OOP)中,程序的设计和实现是围绕着对象的概念展开的。

对象是一个具体的实例,它具有唯一的标识、状态和行为。面向对象编程将数据和对数据的操作封装在一个对象中,通过定义类来创建这些对象。类是对象的模板或蓝图,它描述了对象具有的属性和方法。

面向对象编程的三个主要原则是封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)。

  • 封装:封装将数据和基于数据的操作封装在一个对象中,隐藏了对象的内部细节,只暴露必要的接口供外部使用。这提高了代码的可维护性和安全性。
  • 继承:继承允许一个类(子类)继承另一个类(父类)的属性和方法,使子类具有父类的特性。通过继承,可以实现代码的重用和层次化的组织。
  • 多态:多态允许使用相同的接口来处理不同类型的对象,具有不同的实现。这使得可以通过统一的方式调用不同类的方法,增加了代码的灵活性和可扩展性。

面向对象编程的优点包括代码的模块化、可重用性、可维护性、灵活性和易于理解。它提供了一种更直观、自然的方法来组织和处理复杂的软件系统。许多编程语言,如Java、C++和Python,都支持面向对象编程。

C++ 中的对象

在C++中,对象是类的实例化。类是一种自定义的数据类型,描述了对象具有的属性和行为。通过创建类的对象,可以在内存中分配对应的存储空间,并使用这些对象来访问类中定义的成员。

在C++中,对象由类定义的一组属性和方法构成。属性(成员变量)表示对象的状态或数据,而方法(成员函数)表示对象的行为或操作。通过对象可以访问和操作这些属性和方法,以实现所需的功能。

以下是在C++中创建对象的基本步骤:

  1. 定义类:使用类关键字和类名定义一个类。在类中,可以声明和定义需要的成员变量和成员函数。
  2. 创建对象:使用类名和变量名声明一个对象。声明时会自动调用类的构造函数来初始化对象。
  3. 访问成员:通过使用对象名和成员运算符(点号 .)来访问对象的属性和方法。可以使用对象名访问成员变量并修改其值,也可以调用对象的成员函数执行相应的操作。
  4. 销毁对象:在不再需要对象时,可以使用 delete 关键字来销毁对象,并释放相关的内存空间。销毁对象时会自动调用类的析构函数。

这是一个简单的C++代码示例,演示了定义类、创建对象、访问成员属性和成员方法以及销毁对象的过程:

#include <iostream>
using namespace std;
// 定义一个类
class MyClass {
public:
  // 成员变量
  int myNumber;
  
  // 构造函数
  MyClass() {
    cout << "对象已创建" << endl;
  }
  
  // 成员函数
  void setNumber(int number) {
    myNumber = number;
  }
  
  void printNumber() {
    cout << "My number is: " << myNumber << endl;
  }
  
  // 析构函数  // 析构函数中包含了删除对象的方法,调用析构函数就是删除对象。
  ~MyClass() {
    cout << "对象已销毁" << endl;
  }
};
int main() {
  // 创建对象
  MyClass obj;
  
  // 访问成员属性
  obj.myNumber = 42;
  
  // 访问成员方法
  obj.printNumber();
  
  // 设置成员属性并打印
  obj.setNumber(88);
  obj.printNumber();
  
  return 0;
}

在上面的示例中,我们首先定义了一个名为MyClass的类,该类具有一个整数类型的成员变量myNumber和三个成员函数:一个构造函数用于对象的初始化,一个setNumber()函数用于设置成员属性的值,一个printNumber()函数用于打印成员属性的值。同时,我们还定义了一个析构函数,在对象被销毁时自动调用。

在主函数中,我们先创建一个MyClass类的对象obj,在对象创建时会调用构造函数。然后,通过对象名和成员运算符访问成员变量myNumber并进行赋值。之后,我们调用成员方法printNumber()来打印该数字。

接下来,我们使用成员方法setNumber()设置成员属性的值为88,并再次调用printNumber()打印更新后的值。

最后,在程序结束时,对象obj会被自动销毁,析构函数会被调用。

C++ 面向对象:封装

下面是一个用C++演示封装的代码示例:

#include <iostream>
#include <string>
using namespace std;
// 定义一个类
class Person {
private:
  string name;
  int age;
  
public:
  // 设置姓名
  void setName(string personName) {
    name = personName;
  }
  
  // 获取姓名
  string getName() {
    return name;
  }
  
  // 设置年龄
  void setAge(int personAge) {
    if (personAge >= 0) {
      age = personAge;
    }
  }
  
  // 获取年龄
  int getAge() {
    return age;
  }
};
int main() {
  // 创建对象
  Person person;
  
  // 使用公共接口设置和获取私有属性
  person.setName("Alice");
  person.setAge(25);
  
  // 打印私有属性的值
  cout << "Name: " << person.getName() << endl;
  cout << "Age: " << person.getAge() << endl;
  
  return 0;
}

在上述代码中,定义了一个Person类,并使用private访问修饰符将成员变量nameage声明为私有。这意味着只有类内部可以直接访问这些成员。

然后,在公共区域(public)定义了用于设置和获取这些私有成员的公共成员函数。通过调用setName()setAge()方法,可以设置私有成员变量的值。而使用getName()getAge()方法,可以获取私有成员的值】】

main()函数中,创建一个Person对象,并使用公共接口设置和获取私有属性。通过这种方式,实现了封装的概念,隐藏了类内部的实现细节,使外部代码只能通过公共接口来访问和操作私有属性。

最终输出结果为:

Name: Alice
Age: 25

C++ 面向对象:继承

下面是一个用C++演示继承的代码示例:

#include <iostream>
#include <string>
using namespace std;
// 基类
class Animal {
protected:
  string name;
  
public:
  // 构造函数
  Animal(string animalName) : name(animalName) {}
  
  // 成员函数
  void eat() {
    cout << name << " is eating." << endl;
  }
};
// 派生类
class Cat : public Animal {
private:
  string color;
  
public:
  // 构造函数
  Cat(string catName, string catColor) : Animal(catName), color(catColor) {}
  
  // 成员函数
  void meow() {
    cout << name << " is meowing." << endl;
  }
  
  void displayColor() {
    cout << "The color of " << name << " is " << color << "." << endl;
  }
};
int main() {
  // 创建派生类对象
  Cat cat("Tom", "white");
  
  // 调用基类成员函数
  cat.eat();
  
  // 调用派生类成员函数
  cat.meow();
  cat.displayColor();
  
  return 0;
}

在上述代码中,定义了一个基类Animal和一个派生类Cat。基类有一个受保护的成员变量name,并且包含了一个构造函数和一个eat()成员函数。

派生类Cat继承自基类Animal,并添加了一个私有成员变量color,还定义了两个成员函数meow()displayColor()

main()函数中,创建一个Cat对象,并分别调用基类的成员函数和派生类的成员函数。由于继承关系,派生类可以直接使用基类的成员函数。此外,派生类还可以拥有自己新增的成员变量和成员函数。

最终的输出结果为:

Tom is eating.
Tom is meowing.
The color of Tom is white.

C++ 面向对象:多态

以下是一个使用C++演示多态的代码示例:

#include <iostream>
#include <string>
using namespace std;
// 基类
class Shape {
protected:
  string name;
  
public:
  // 构造函数
  Shape(string shapeName) : name(shapeName) {}
  
  // 纯虚函数
  virtual void draw() = 0;
};
// 圆形派生类
class Circle : public Shape {
private:
  double radius;
  
public:
  // 构造函数
  Circle(string circleName, double circleRadius) : Shape(circleName), radius(circleRadius) {}
  
  // 实现纯虚函数
  void draw() override {
    cout << "Drawing a circle: " << name << ", radius: " << radius << endl;
  }
};
// 三角形派生类
class Triangle : public Shape {
private:
  double base;
  double height;
  
public:
  // 构造函数
  Triangle(string triangleName, double triangleBase, double triangleHeight) : Shape(triangleName), base(triangleBase), height(triangleHeight) {}
  
  // 实现纯虚函数
  void draw() override {
    cout << "Drawing a triangle: " << name << ", base: " << base << ", height: " << height << endl;
  }
};
int main() {
  // 创建基类指针数组
  Shape* shapes[2];
  
  // 创建圆形对象和三角形对象
  Circle circle("Circle", 5.0);
  Triangle triangle("Triangle", 4.0, 3.0);
  
  // 将对象指针赋给数组中的元素
  shapes[0] = &circle;
  shapes[1] = &triangle;
  
  // 循环调用虚函数
  for (int i = 0; i < 2; i++) {
    shapes[i]->draw();
  }
  
  return 0;
}

在上面的代码示例中,我们定义了一个基类Shape,它包含一个纯虚函数draw()。然后我们派生出两个具体的形状类:CircleTriangle,并分别实现了draw()函数。

main()函数中,我们首先创建了一个基类指针数组shapes,然后通过创建不同的派生类对象给数组元素赋值。

接下来,我们使用循环遍历数组,并通过基类指针调用虚函数draw()。由于虚函数是动态绑定的,它会根据对象的真实类型调用相应的成员函数,从而实现多态。

运行代码,输出结果为:

Drawing a circle: Circle, radius: 5
Drawing a triangle: Triangle, base: 4, height: 3

可以看到,虽然使用的是基类的指针数组,但通过调用虚函数draw()时,会根据对象的类型来执行相应的成员函数,实现了多态的效果。

对象的属性是对象

在面向对象的程序设计中,有时候会将一个对象的属性设计为另一个对象,这种情况通常发生在以下两种情况下:

  1. 对象之间存在"has-a"关系:当一个对象在概念上包含或组成另一个对象时,适合将该对象的属性设计为另一个对象。例如,在汽车和轮胎之间存在"has-a"关系,因为汽车包含四个轮胎;在学生和课程之间存在"has-a"关系,因为学生选修多门课程。
class Tire {
  // 轮胎类的定义
};
class Car {
private:
  Tire tire;  // 将轮胎作为汽车的属性
public:
  // 汽车类的其他成员函数和成员变量
};
class Course {
  // 课程类的定义
};
class Student {
private:
  Course courses[5];  // 将课程数组作为学生的属性
public:
  // 学生类的其他成员函数和成员变量
};
  1. 需要在不同对象之间共享数据:当多个对象需要共享一组数据时,适合将这组数据封装为一个对象,并将其作为其他对象的属性。这样可以确保对象之间共享的数据一致性。
class Location {
private:
  double longitude;
  double latitude;
public:
  // Location类的其他成员函数和构造函数
};
class Person {
private:
  string name;
  Location location;  // 将位置信息作为人的属性
public:
  // Person类的其他成员函数和构造函数
};

通过将对象作为另一个对象的属性,可以提高代码的可读性和可维护性。同时,这也符合面向对象的封装和组合原则,使得程序更加模块化和灵活。

C++ 对象序列化和反序列化

对象序列化和反序列化是将对象转换为字节流(序列化)或将字节流转换回对象(反序列化)的过程。在C++中,可以使用标准库提供的序列化和反序列化功能来实现。

对象序列化

要将对象序列化为字节流,可以使用std::ofstream来将对象数据写入文件。需要注意的是,被写入文件的对象的类必须实现序列化操作符<<

以下是一个示例:

#include <iostream>
#include <fstream>
class Foo {
private:
  int data;
  
public:
  // 序列化操作符
  friend std::ostream& operator<<(std::ostream& os, const Foo& obj) {
    return os.write(reinterpret_cast<const char*>(&obj.data), sizeof(obj.data));
  }
  
  // 构造函数
  Foo(int d) : data(d) {}
};
int main() {
  Foo foo(42);
  
  std::ofstream file("data.bin", std::ios::binary);
  if (file) {
    file << foo;  // 序列化对象到文件
    file.close();
  }
  
  return 0;
}

在上述示例中,定义了一个类Foo,其中包含一个data成员变量。通过重载<<操作符,将data写入指定的输出流。在main()函数中,创建了一个Foo对象,并将其序列化到名为"data.bin"的二进制文件中。

对象反序列化

要从字节流中反序列化对象,可以使用std::ifstream从文件中读取数据。被读取的对象的类必须实现反序列化操作符>>

以下是一个示例:

#include <iostream>
#include <fstream>
class Foo {
private:
  int data;
  
public:
  // 反序列化操作符
  friend std::istream& operator>>(std::istream& is, Foo& obj) {
    return is.read(reinterpret_cast<char*>(&obj.data), sizeof(obj.data));
  }
  
  // 构造函数
  Foo() = default;
  
  // 打印数据
  void printData() {
    std::cout << "Data: " << data << std::endl;
  }
};
int main() {
  Foo foo;
  
  std::ifstream file("data.bin", std::ios::binary);
  if (file) {
    file >> foo;  // 从文件反序列化对象
    file.close();
  }
  
  foo.printData();  // 打印反序列化后的数据
  
  return 0;
}

在上述示例中,定义了一个与序列化示例相同的类Foo。通过重载>>操作符,将数据从输入流中读取到data成员变量中。在main()函数中,创建了一个Foo对象,并从名为"data.bin"的二进制文件中反序列化它。最后,调用printData()函数打印反序列化后的数据。

注意 ,在序列化和反序列化过程中,使用的输入流和输出流都应以二进制模式打开。这可以通过将std::ios::binary选项传递给std::ofstreamstd::ifstream的构造函数来实现。

关注我,不迷路,共学习,同进步

关注我,不迷路,共学习,同进步

相关文章
|
1天前
|
存储 编译器 C++
|
1天前
|
算法 数据处理 C++
|
1天前
|
数据安全/隐私保护 C++
|
1天前
|
存储 开发框架 Java
|
1天前
|
存储 编译器 数据安全/隐私保护
|
1天前
|
C++
C++基础知识(四:类的学习)
类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。
|
1天前
|
算法 C++ 容器