Cpp面向对象

C++核心

类和对象

  1. 类和对象

类和对象是C++面向对象的基础,在C++中万事万物都是对象,C++利用类来实例化对象,下面是创建一个Circle类并实例化的语法:

1
2
3
4
5
6
7
8
9
10
11
12
// 创建类
class Circle {
public:
int m_r;

void getM_r {
cout << m_r;
}
};

// 实例化
Circle a;

类中的变量称为属性(成员属性),类中的函数称为行为(成员函数、成员方法)。

  1. 访问权限

public: 类内可以访问,类外可以访问

protected: 类内可以访问,类外不可以访问

private: 类内可以访问,类外不可以访问

protected 和 private在继承部分会有不同。

  1. struct和class的区别

默认访问权限不同,struct默认访问权限是public,class默认权限是private

  1. 在C++中一般把成员属性设置为私有,把成员方法设置公共。下面是一个标准的C++类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Person {
private:
string name;
int val;

public:
string getName() {
return name;
}

void setName(string s) {
name = s;
}

int getVal() {
return val;
}

void setVal(int x) {
val = x;
}
};

// 实例化
Person a;
a.setName("小明");
a.setVal(100);

cout << a.getName() << endl;
cout << a.getVal();

通过成员函数来访问和设置成员属性,可以防止误操作。

  1. 对象的初始化和清理

C++通过构造函数析构函数来进行初始化和清理,这两个函数由编译器自动调用,同时存在默认实现。

构造函数:创建对象时给对象赋值

析构函数:对象销毁前,系统自动调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 构造函数
类名(){}
// 析构函数
~类名(){}

class Person {
private:
int val;

public:
Person() {
// 无参构造
}
Person(int x) {
// 含参构造
val = x;
}

~Person() {
// 不允许有参数,也不允许发生重载
// 对象销毁前自动调用
}
};
  1. 构造函数的分类及调用

分类:无参构造(默认构造),有参构造、拷贝构造

拷贝构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person {
private:
int age;

public:
Person() { // 无参构造

}

Person(int a) { // 有参构造

}

Person(const Person& p) { // 拷贝构造
age = p.age;
}
};

调用方式

括号法:

1
2
3
Person p1; // 默认构造调用
Person p2(10); // 有参构造
Person p3(p2); // 拷贝构造

Person p(); 不允许这样调用无参构造,尽管编译不报错,但在运行时,会将这一句误解为函数声明

显示法:

1
2
3
4
5
6
7
Person p1; // 默认构造
Person p2 = Person(10); // 有参构造
Person p3 = Person(p2); // 拷贝构造

// Person(10) 会被认为是一个匿名对象,执行结束后,系统会立即回收匿名对象。

// Person(p3); 这样是错误的。不要利用拷贝构造,创建匿名对象,会被认为是一个对象的声明

隐式转换法:

1
2
3
Person p1; // 默认构造
Person p2 = 10; // 有参构造
Person p3 = p2; // 拷贝构造

new开辟的数据在堆区

  1. 浅拷贝与深拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person {
private:
string name;
int* t;

public:
Person() {}
Person(string _name, int _t) {
name = _name;
t = new int(_t); // 开辟在堆区
}
Person(const Person& p) {
name = p.name;
t = p.t; // 编译器提供的默认拷贝形式(浅拷贝)
t = new int(*p.int); // 深拷贝
}
};

浅拷贝会造成堆区内存重复释放。

  1. 初始化列表
1
2
3
4
5
6
7
8
class Person {
private:
string name;
Person* next;

public:
Person(string _name, Person* _next) : name(_name), next(_next) {}
};

9、静态成员

静态成员变量:

  • 所有对象共享同一份数据
  • 编译阶段分配内存(全局区)
  • 类内声明,类外初始化
1
2
3
4
5
6
7
class person { // 类内声明
private:
static int a;
};

int person::a = 100; // 类外初始化
// 静态成员变量也有访问权限

静态成员函数:

  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量

对象特性

  1. C++对象模型和this指针
  • 在C++中,类内的成员变量和成员函数分开存储
  • 只有非静态成员变量才属于类的对象上
1
2
// 一个对象占多少内存,取决于它的成员变量
// 空对象占一个字节
  1. this指针
  • this指针指向被调用的成员函数所属的对象
1
2
3
4
5
6
7
8
9
class person {
public:
string name;

person(string name) {
// 解决名称冲突问题
this->name = name;
}
}
  1. 空指针访问成员函数
1
2
// 空指针可以访问函数
// 但是不能访问其中的属性
  1. const修饰成员函数
  • 常函数内不能修改成员属性
  • 常对象只能调用常函数
  • this的本质是指针常量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
public:
void showPerson() const {
m_A = 100; // 非法
m_B = 200; // 合法
}

int m_A;
mutable int m_B;
}

void test02() {
const Person p;
p.m_A = 100; // 非法
p.m_B = 200; // 合法
}

友元

在程序中,有些私有属性,也想让类外的特殊函数访问

  1. 全局函数做友元
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Building {
friend void fun(Building build); // 声明友元全局函数
public:
Building() {}
public:
string sittingRoom;
private:
string room;
};

void fun(Building Build) {
cout << build.sittingRoom; // 合法
cout << build.room; // 非法
}
  1. 类做友元
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Goodgay {
public:
Building* build = new Building();

void visit() {
cout << build->room; // 非法
cout << build->sittingRoom; // 合法
}
}
class Building {
friend class Goodgay;
public:
Building() {}
public:
string sittingRoom;
private:
string room;
};
  1. 成员函数做友元
1
friend void Goodgay::visit();

运算符重载

对已有的运算符重新进行定义,赋予其另一种功能

  1. 加号重载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person {
public:
int m_a;
}
Person operator+ (Person& p1, Person& p2) {
// 重载+号
Person temp;
temp.m_a = p1.m_a + p2.m_a;
return temp;
};
Person s1;
s1.m_a = 10;
Person s2;
s2.m_a = 20;
Person p3 = p1 + p2; // 非法操作,编译器无法理解P1 + p2
  1. 左移运算符
1
2
3
4
5
6
7
8
9
10
11
// 重载 << 可以输出类
class Person {
public:
int m_a;
};
void operator<< (ostream cout, Person& p) {
// 重载<<号
cout << p.m_a;
}
Person s1;
cout << s1; // 非法
  1. 递增运算符重载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyInteger {
public:
int number;
MyInteger() {number = 0;}
};
// 重载前置++
void operator++ (MyInteger& p) {
p.number++;
}
// 后置++
void operator++ (int) {

}
MyInteger p;
p ++;
  1. 赋值运算符重载

编译器会对’=’进行默认重载,对属性进行值拷贝,可能会带来浅拷贝的问题

  1. 关系运算符重载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Person {
public:
int age;
string name;
};

bool operator== (Person& p1, Person& p2) {
if (p1.age == p2.age && p1.name == p2.name) {
return ture;
} else {
return false;
}
}

Person p1(10, "tom");
Person p2(100, "tom");

if (p1 == p2) // 非法,需要重载==号
  1. 函数调用重载

在STL中常用函数调用重载,也叫仿函数

继承

  1. 语法

class 子类 : 继承方式 父类

1
2
3
4
class son : public father {
public:
cout << "这是一个子类" << endl;
};
  1. 继承方式

父类中私有的内容,子类无法访问

  • 公共继承

父类中public变为public,protected变为protected

  • 保护继承

父类中public变为protected,protected变为protected

保护权限,类外不可以访问

  • 私有继承

父类中public变为private,protected变为private

  1. 继承中的对象模型

父类中所有非静态成员属性都会被子类继承下去

父类中私有成员属性,是被编译器隐藏了

  1. 构造和析构顺序

子类继承父类后,当创建子类对象时,也会调用父类的构造

先构造父类,再构造子类,先析构子类,再析构父类

  1. 继承中同名的处理方式
  • 访问子类 直接访问
  • 访问父类 +作用域
  1. 同名静态成员处理

跟5一样,子类直接访问,父类+作用域

子类中出现和父类同名静态成员函数,会隐藏掉父类的所有静态成员函数

son.father::fun()

  1. 多继承语法

语法:class 子类 : 继承方式 父类1,继承方式 父类2

  1. 虚继承

class sheep : virtual public animal {

};

菱形继承导致了子类继承同样的数据,造成内存浪费

virtual可以解决菱形继承的问题

vbptr 虚基类指针 指向 vbtable 虚基类表

多态

  • 静态多态:函数重载 运算符重载 复用函数名
  • 动态多态:派生类和虚函数实现运行时多态

静态多态的函数地址早绑定,编译阶段确定函数的地址

动态多态的函数地址晚绑定,运行阶段确定函数的地址

父类的指针,可以直接指向子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class animal {
public:
void speak() {
cout << "动物在说话"
}
};

class cat {
public:
void speak() {
cout << "猫在说话" << endl;
}
};

void doSpeak(animal& a) {
a.speak();
}

int main() {
cat a;
doSpeak(a);
}
// 输出->动物在说话
将父类中的函数换位 virtual void speak()
// 输出->猫在说话

动态多态的两个条件

  • 存在继承关系
  • 子类重写父类的虚函数
  • 父类的指针或引用指向子类对象

底层原理

virtual函数中存在一个vfptr-虚函数表指针

指向vftable

纯虚函数

当类中有了纯虚函数,这个类就是抽象类

抽象类不可以实例化

子类必须重写抽象类中的纯虚函数,否则也属于抽象类

1
2
3
4
class animal {
public:
virtual void fun() = 0; // 纯虚函数
};

虚析构和纯虚析构

把父类中的析构函数改为虚析构

利用虚析构可以解决父类指针释放子类对象,释放不干净的问题

纯虚析构

virtual ~animal() = 0;

需要在类外,重写析构函数

文件读写

文件流管理

1、文本文件

文件以文本的ASCII码形式存储在计算机中

2、二进制文件

文本以二进制形式存储在计算机中,用户一般不能直接读懂

  • ofstream 写操作
  • ifstream 读操作
  • fstream 读写操作

写文件步骤:

  1. 包含头文件
  2. 创建流对象
  3. 打开文件

ios::in 读文件的方式

ios::out 写文件的方式

ios::trunc 如果文件存在先删除,再创建

  1. 写数据

  2. 关闭文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <fstream>

using namespace std;

// 写文件
void test01() {
ofstream ofs;

ofs.open("文件路径", ios::out);

ofs << "你好,文本" << endl;

ofs.close();
}

void test02() {
// 读文件
ifstream ifs;

ifs.open("hello.txt", ios::in);

if (!ifs.is_open()) {
cout << "打开失败" << endl;
return;
}

char buf[1024] = {0};
while (ifs >> buf) {
cout << buf << endl;
}

string buf;
while (getline(ifs, buf)) {
cout << buf << endl;
}

char c;
while ((c = ifs.get() != EOF)) {
cout << c;
}
}

int main() {
test01();
}

// 二进制方式读写

二进制写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <fstream>

using namespace std;

class Person {
public:
char m_Name[64];
int m_Age;
};

void test01() {
ofstream ofs;
ofs.open("wang.txt", ios::out | ios::binary);
Person p;
p.m_Name = "zhangsan";
p.m_Age = 18;

ofs.write((const char*)&p, sizeof(Person));

ofs.open();
}

int main() {
test01();
}

二进制读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <fstream>

using namespace std;

class Person {
public:
char m_Name[64] = "zhaolei";
int m_Age;
};

void test02() {
ofstream ofs;
ofs.open("wang.txt", ios::out | ios::binary);
Person p;
p.m_Name = "zhangsan";
p.m_Age = 18;

ofs.write((const char*)&p, sizeof(Person));

ofs.open();
}

int main() {
test01();
}

Cpp面向对象
http://example.com/2025/03/07/cpp_base/
Author
yuanyuan
Posted on
March 7, 2025
Licensed under