C++ 面向对象-class的基本使用

本文最后更新于:2022年4月22日 上午

C++ 面向对象-class的使用

C++中也有面向对象(oop) 的概念,面向对象的三大特性是 封装,继承,多态 ,基本上大学都会学习面向对象思想,这里我们先从最基本的语法开始学习,会涉及到封装 后续再更新继承与多态,因为多态的前提是继承,所以后面会先学习继承,再学习多态 。

通过 class关键字来创建一个类,类里面可以通过以下三个关键字来修饰 属性或者方法

  • public 公共属性,类内部,派生类,实例都可以访问

  • private 私有属性,类内部才可以访问

  • protected 类内部和派生类可以访问,实例不可以访问

类的定义

以下定义了一个最基本的 Person类,并且定义了两个公共属性

class Person {
public:
  string name;
  int age;
}

类的基本使用

class Person {
public:
  string name;
  int age;
}

int main() {

  // 类的基本使用 和 struct 使用起来差不多
  Person p1;
  p1.name = "小明";
  p1.age = 18;

}

封装

我们通过 private关键字进行修饰,然后编写对应的 gettersetter方法来对这些私有属性进行赋值与获取。

class Person2 {

// 私有的
private:
  string name;
  int age;

// 通过 get/set 来操作私有属性
public:
  void setName(string props_name) {
    name = props_name;
  }

  string getName() {
    return name;
  }

  void setAge(int props_age) {
    age = props_age;
  }

  int getAge() {
    return age;
  }
};

int main() {

  // 封装 getter 和 setter
  Person2 p2;
  p2.setName("小红");
  p2.setAge(18);
  
}

输出

小红
18

class 和 struct 的区别

从使用的角度来看,classstruct 没有什么区别

// 默认的权限是 private
class Person {
  string name;
  int age;
};


struct Student {
  string name;
  int age;
};

int main() {
  Person3 p3;
  
  // 语法报错,不允许设置
  //p3.name = "小红";
  //p3.age = 18;
  
  // 相当于是 public 权限
  Student s1;
  s1.name = "小李";
  s1.age = 19;
  
  return 0;
}

class 基本案例

计算两个立方体的体积是否一致

#include <iostream>;
using namespace std;

class Cube {

private:
  int length;
  int width;
  int height;

public:
  void setLength(int len) {
    length = len;
  }

  int getLength() {
    return length;
  }

  void setWidth(int w) {
    width = w;
  }

  int getWidth() {
    return width;
  }

  void setHeight(int h) {
    height = h;
  }

  int getHeight() {
    return height;
  }

  // 计算面积
  int calcArea() {
    return length * width;
  }

  // 计算体积
  int calcVolume() {
    return length * width * height;
  }

  // 比较两个立方体是否相同
  bool isSomeCube(Cube c) {
    if (height == c.getHeight()
      && width == c.getWidth()
      && length == c.getLength()
    ) {
      return true;
    }

    return false;
  }
};


// 比较两个立方体是否相同
bool isSomeCule(Cube& c1, Cube& c2) {

  if (c1.getHeight() == c2.getHeight()
    && c1.getWidth() == c2.getWidth()
    && c1.getLength() == c1.getLength()
  ) {
    return true;
  }

  return false;
}

int main() {
  
  /*
    案例一:计算两个立方体是否相同
  */
  Cube c1;
  c1.setHeight(10);
  c1.setLength(10);
  c1.setWidth(10);

  cout << "c1 面积:" << c1.calcArea() << endl;
  cout << "c1 体积:" << c1.calcVolume() << endl;



  Cube c2;
  c2.setHeight(10);
  c2.setLength(10);
  c2.setWidth(10);

  cout << "c2 面积:" << c1.calcArea() << endl;
  cout << "c2 体积:" << c1.calcVolume() << endl;

  cout << "全局方法比较 c1 和 c2 是否一致:" << isSomeCule(c1, c2) << endl;
  cout << "实例方法比较 c1 和 c2 是否一致:" << c1.isSomeCube(c2) << endl;

  system("pause");
  return 0;
}

输出

c1 面积:100
c1 体积:1000
c2 面积:100
c2 体积:1000
全局方法比较 c1 和 c2 是否一致:1
实例方法比较 c1 和 c2 是否一致:1

如果我们将上述代码简单的进行一下修改

Cube c1;
 c1.setHeight(10);
 c1.setLength(10);
 c1.setWidth(10);

 cout << "c1 面积:" << c1.calcArea() << endl;
 cout << "c1 体积:" << c1.calcVolume() << endl;



 Cube c2;
 c2.setHeight(10);
 c2.setLength(10);
 c2.setWidth(20);

 cout << "c2 面积:" << c1.calcArea() << endl;
 cout << "c2 体积:" << c1.calcVolume() << endl;

 cout << "全局方法比较 c1 和 c2 是否一致:" << isSomeCule(c1, c2) << endl;
 cout << "实例方法比较 c1 和 c2 是否一致:" << c1.isSomeCube(c2) << endl;

输出

c1 面积:100
c1 体积:1000
c2 面积:100
c2 体积:1000
全局方法比较 c1 和 c2 是否一致:0
实例方法比较 c1 和 c2 是否一致:0

类的分文件编写

我们会发现,在上面,我们所有的代码都是写在一个文件里面的,当代码多起来后维护起来会很困难,所以我们分文件编写,和之前的 函数的分文件编写差不多,如下所示

|- 头文件
  |- person.h
|- 源文件
  |- person.cpp
  |- main.cpp

person.h

#pragma once
#include <iostream>;
using namespace std;
#include "phone.h"

class Person {

private:
  string name;
  int age;

public:
  Phone phone;


  void setName(string name);
  string getName();

  void setAge(int age);
  int getAge();

  void setPhone(Phone pPhone);
  Phone getPhone();

  void sayName();
  void sayAge();
  void sayBrand();
};

class 的声明与实现分开时,在写实现时需要通过 XXX::xx 的语法表示实现的哪一个方法

person.cpp

#include "person.h";
#include "phone.h";

void Person::setName(string pName) {
  name = pName;
}

string Person::getName() {
  return name;
}


void Person::setAge(int pAge) {
  age = pAge;
}

int Person::getAge() {
  return age;
}

void Person::setPhone(Phone pPhone) {
  cout << pPhone.brand << endl;
  phone = pPhone;
}

Phone Person::getPhone() {
  return phone;
}

void Person::sayName() {
  cout << "我的姓名是:" << name << endl;
}

void Person::sayAge() {
  cout << "我的年龄是:" << age << endl;
}

void Person::sayBrand() {
  cout << "我的手机是:" << phone.brand << endl;

}

phone.h

#pragma once
#include <iostream>;
using namespace std;

class Phone {

public:
  string brand;

};

main

#include <iostream>;
using namespace std;
#include "person.h";
#include "phone.h";

// 类的嵌套,以及类的分文件编写
int main() {

  Person p;

  p.setName("小明");
  p.setAge(18);

  Phone phone;
  //phone.setBrand("华为");
  phone.brand = "华为";

  
  p.setPhone(phone);

  cout << "名字:" << p.getName() << endl;
  cout << "年龄:" << p.getAge() << endl;
  cout << "手机:" << p.getPhone().brand << endl;

  p.sayName();
  p.sayAge();
  p.sayBrand();

  system("pause");
  return 0;
}

构造函数&析构函数

构造函数

和很多语言一样,C++也有构造函数,会在实例化对象的时候调用,所有的 class 都有构造函数,如果没有手动编写的话默认编译器会帮我默认生成一个空实现(内部没有任何代码)的构造函数

  • 与类同名,不需要写 返回值类型,也不允许 return

  • 可以重载

析构函数

会在函数销毁的时候调用(类似于 vue 生命周期中的 beforeDistory

  • ~开头,后面与类名同名,不允许重载,没有返回值

编写 构造函数和析构函数时都需要使用 public关键字进行修饰

构造的分类

  1. 按照参数进行分类

&ensp;&ensp;&ensp;&ensp;1. 无参构造函数(默认构造函数)

&ensp;&ensp;&ensp;&ensp;2. 有参构造函数

  1. 按照类型分类

&ensp;&ensp;&ensp;&ensp;1. 普通构造函数

&ensp;&ensp;&ensp;&ensp;2. 拷贝构造函数

无参构造函数(默认构造函数) 的基本使用

如下代码 创建p1时会调用 Person的默认构造函数,虽然没有手动写,但是编译器会默认帮我们提供一个

class Person {

public:
  string name;
  
  // 默认构造函数
  Person() {
    cout << "Person 默认构造函数被调用" << endl;
  }
  
  ~Person() {
    cout << name <<": Person 析构函数被调用" << endl;
  }
  
};

// ...

Person P1;
P1.name = "小明";

输出

Person 默认构造函数被调用
小明: Person 析构函数被调用

有参构造函数

当我们需要在创建对象时同时传入一些参数的话,就可以使用有参构造函数了

class Person {

public:
  string name;

  // 默认构造函数
  Person() {
    cout << "Person 默认构造函数被调用" << endl;
  }

  // 有参构造函数
  Person(string pName) {
    cout << "Person 有参构造函数被调用" << endl;
    name = pName;
  }

  ~Person() {
    cout << name <<": Person 析构函数被调用" << endl;
  }
  
};

// ...

Person p1("小明");
cout << "名称:" << p1.name << endl;

输出

Person 有参构造函数被调用
名称:小明
小明: Person 析构函数被调用

拷贝构造函数

C++ 中还有拷贝构造函数的概念,可以用来拷贝一个相同的对象,如果不写的话,编译器默认也会提供一个

class Person {

public:
  string name;

  // 默认构造函数
  Person() {
    cout << "Person 默认构造函数被调用" << endl;
  }
  
  // 有参构造函数
  Person(string pName) {
    cout << "Person 有参构造函数被调用" << endl;
    name = pName;
  }
    
  Person(const Person& p) {
    cout << "Person 拷贝构造函数被调用" << endl;
    name = p.name;

  }

  ~Person() {
    cout << name <<": Person 析构函数被调用" << endl;
  }

};

// ...

Person p1("小明");
Person p2(p1);

cout << "p2 姓名:" << p2.name << endl;

输出

Person 有参构造函数被调用
Person 拷贝构造函数被调用
p2 姓名:小明
小明: Person 析构函数被调用
小明: Person 析构函数被调用

构造函数的三种调用方式

C++中,构造函数有三种调用的方式,也就是说其实我们可以通过三种方式来实例化对象

方式一

Person p;
Person p2("小明");

方式二

Person p3 = Person();
Person p4 = Person("小明");

方式三 (隐式转换方式)

string name = "小明";
Person p5 = name;

注意:方式一中调用默认构造函数不可以加上小括号() ,如果加上小括号会被编译器当成是 方法的声明如:Person p()

构造函数的三个执行时机

实例化对象时的基本使用

Person p1("小明");
Person p2(p1);

输出

Person 有参构造函数被调用
Person 拷贝构造函数被调用

对象作为函数参数时使用

void doWork(Person p) {

}

// ...

// 接上述代码继续使用
doWork(p2);

输出

Person 拷贝构造函数被调用
小明: Person 析构函数被调用

作为函数的返回值使用

Person doWork2() {
  // 这里会执行两次构造函数,第一次为 定义p,第二次为返回 p 的时候
  Person p("小明");
  return p;
}

// ...

doWork2();

输出

Person 有参构造函数被调用
Person 拷贝构造函数被调用

这里也可以把修改一下上述代码,让它只执行一次,如下

Person doWork2() {
  return Person p("小明");
}

输出

Person 有参构造函数被调用

构造函数执行时机的完整示例

// 作为函数参数
void doWork(Person p) {

}

// 作为函数返回值
Person doWork2() {
  return Person("小明");
}

// ...

// 1. 基本使用
Person p1("小明");
Person p2(p1);

cout << "------------------" << endl;

// 2. 作为函数参数
doWork(p2);

// 3. 函数的返回值
cout << "------------------" << endl;
doWork2();

输出

Person 有参构造函数被调用
Person 拷贝构造函数被调用
------------------
Person 拷贝构造函数被调用
小明: Person 析构函数被调用
------------------
Person 有参构造函数被调用
小明: Person 析构函数被调用
小明: Person 析构函数被调用
小明: Person 析构函数被调用

总结

上述是 classC++中的基本使用,掌握以上语法后,就已经可以编写一些基本的案例了,需要注意的是 构造函数的几种调用方式,以及 普通构造函数与拷贝构造函数的区别,析构函数的执行时机,C++的面向对象还有很多需要掌握的知识,后续再慢慢进行记录更新吧