4-面向对象

面向对象具有三大特性,继承、封装、多态

定义类以及实例化

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
const double PI = 3.14;

//定义一个类
class Circle {

//定义公共属性和方法
public:
//属性:半径
int m_r;
//声明方法: 计算周长
double calculateLength();
};

//类方法的实现
double Circle::calculateLength()
{
return m_r * PI * 2;
}


int main(int argc, char const *argv[])
{
//实例化Circle类,可以使用其可使用的属性和方法
Circle circle;
circle.m_r = 10;
cout<<"周长为:"<<circle.calculateLength();
return 0;
}

封装

设计类时,应该针对类的使用场景封装对应的成员,访问权限有三种:

  • 公开: public :类外可以访问
  • 保护: protected :类内可以访问,子类可以访问,类外不能访问
  • 私有: privite : 只有当前类内可以访问

C++中struct和class的区别

  • struct默认访问权限为 public
  • class默认访问权限为 private

一般在开发中,会将成员属性设置为私有,并按需对外提供get/set方法

构造函数和析构函数

C++在对象实例化的时候,会调用构造函数和析构函数对对象进行初始化和销毁操作。如果不做实现,编译器会自动提供空实现。

构造函数语法

  1. 构造函数没有返回值,不写void
  2. 函数名=类名
  3. 构造函数可以有参数,所以可以发生重载
  4. 程序会在调用对象的时候自动调用,只调用一次

析构函数语法

  1. 析构函数没有返回值,不写void
  2. 函数名=~类名

  3. 析构函数不可以有参数,不可以发生重载

  4. 程序会在对象销毁前自动析构,不需要手动调用,只调用一次。

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Person
{
public:
// 构造函数
Person()
{
cout<<"person初始化"<<endl;
}
// 析构函数
~Person()
{
cout<<"person销毁"<<endl;
}
};

int main(int argc, char const *argv[])
{
Person p;
return 0;
}

构造函数分类

两种分类方式:

按参数分为: 有参构造、无参构造

按类型分为:普通构造、拷贝构造(复制构造)

复制构造函数的函数头为: ClassName(const ClassName &param)

  • 在值传递的时候,会自动调用其拷贝构造函数作为形参
  • 在局部变量值返回的时候,会回收局部变量空间,调用拷贝构造函数返回一个新的对象。

三种调用方式:括号、显示法、隐式转换法

  • 不要用括号法调用默认的无参构造,会被认为是一个函数声明。
  • 不要利用拷贝构造函数初始化匿名对象,编译器会认为是一个对象的声明。

构造函数调用规则

默认C++会给一个类添加3个函数

  1. 无参构造
  2. 无参析构
  3. 拷贝构造(对所有属性进行值拷贝)
  • 如果用户定义了有参构造,则不再提供无参构造,但仍然提供拷贝构造
  • 如果用户定义了拷贝构造,则不再提供默认构造

深浅拷贝

浅拷贝: 简单赋值拷贝

深拷贝: 在堆分配空间进行拷贝

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Person{
public:
Person(int age, int height)
{
this->age = age;
this->height = new int(height);
}
//拷贝构造定义
Person(const Person &p)
{
//对于height这一指针,使用深拷贝,在堆中创建新的对象
this->height = new int(*(p.height));
this->age = p.age;
}

~Person()
{
//释放height在堆中的内存
if(height != nullptr)
{
delete height;
height = nullptr;
}
}

int age;
int* height;
};


void test01()
{
Person p1(18, 160);
Person p2 = p1;
cout<<p1.age<<endl;
cout<<p1.height<<endl;

cout<<p2.age<<endl;
cout<<p2.height<<endl;
}

//18
//0x1546068c0 height地址1
//18
//0x1546068d0 height地址2,很明显,在堆中是两个不同的对象

初始化列表

构造函数可以使用类名(参数, 参数, ...):属性(参数),属性(参数)...的方式对对象的值初始化

1
2
3
4
5
6
7
class Person{
public:
Person(int age, int height):age(age),height(new (height){}

int age;
int* height;
};