【C++】14.类与面向对象编程[深蓝学院C++第12章]

前言

 

一.结构体与对象聚合

1.1 结构体

对基本数据结构进行扩展,可以将多个数据放在一起形成一个整体

定义特性:

(1)结构体的声明与定义,注意定义后面要跟分号;

(2)仅有声明的结构体是不完全类型;

(3)结构体(以及类)的一处定义原则:翻译单元级别

1.2 数据成员的声明与初始化

数据域:声明数据成员的区域

声明:

(1)数据成员可以使用decltype来声明其类型,但不能使用auto

(2)数据成员声明时可以引入const、引用来限定

(3)数据成员会在构造对象时定义

初始化:

(1)类内成员初始化

(2)聚合初始化,从初始化列表到指派初始化器,Str str{.x=3,.y=4};

mutable限定符:已经被声明了const的结构体对象,标识了mutable的成员还是可以被改变

静态数据成员:

(1)多个对象之间共享的数据成员;

(2)定义方式存在一定的演化,包括类外定义、const静态成员的类内初始化、内联静态成员的初始化

(3)可以使用auto推导类型

静态数据成员的访问:

(1)“.”和“->”操作符

(2)“::”操作符

 

二.成员函数(方法)

可以在结构体中定义函数,作为其成员的一部分,对内操作数据成员、对外提供调用接口

类:在结构体中将数据与相关的成员函数组合在一起,是C++在C基础上引入的概念

关键字class

类可视为一种抽象数据类型,通过相应的接口进行交互

类本身形成域,称为类域

2.1 成员函数的声明与定义

类内定义(隐式内联),类内声明的函数会自动内联防止重复定义

类内声明+类外定义,在同一个文件中时注意使用inline,在不同文件时注意指定域

类与编译器的两遍处理,先扫一遍成员第二遍再处理逻辑,以便使数据成员和函数的灵活的先后顺序

成员函数与尾随返回类型,trail returning type

2.2 成员函数与this指针

this指针引用当前对象,this是const的不可编辑

编译器在调用成员函数时会隐含地传入一个this参数

基于const的成员函数重载,对外提供的接口不希望修改类内数据成员,void fun() const

2.3 成员函数的名称查找与隐藏关系

函数内部(包括形参名称)隐藏函数外部的符号

类内部名称隐藏类外部的符号

使用this或域操作符引入域外的名称查找 

2.4 静态成员函数

不会传入隐含的this

不能在静态成员函数中使用对象的非静态成员,因为没有this

在静态成员函数中可以返回静态数据成员

三.访问限定符与友元

3.1 关键字与访问限定

关键字:public、private、protected

访问限定的作用:

(1)访问权限的引入使得可以对抽象数据类型进行封装

(2)类与结构体缺省访问权限的区别,结构体缺省为public,类缺省为private

3.2 友元

关键字friend打破访问权限限制

特性:

(1)声明某个类或某个函数是当前类的友元;(慎用,因为封装本来是一种正向设计)

(2)可以在类内首次声明友元类或友元函数,不必再单独声明该类或友元,

(3)友元函数的类外定义与类内定义,虽然在类内定义,但实际上属于类外;

(4)隐藏友元:实参名称查找,提高编译器效率,应用场景见下章的运算符重载之对称运算符部分的讲解

 

四.构造、析构与复制成员函数

4.1 构造函数

构造对象时调用的函数

构造函数特性:

(1)名称与类名相同,无返回值,可以包含多个版本(重载)

(2)C++11起,代理构造函数,默认构造调用自定义的构造,代理构造会先执行,默认构造后执行

初始化列表:区分数据成员的初始化与赋值,初始化需要开辟内存,赋值不需要

xxx(inputVal):member1(inputVal),member2(defaultVal)

初始化列表特性:

(1)通常情况下可以提升系统性能;

(2)一些情况下必须使用初始化列表,如类中包含引用成员,必须在构造时就初始化;

(3)注意元素的初始化顺序与其声明顺序相关,与初始化列表中的顺序无关;手写时尽量将初始化列表与声明顺序一致

(4)使用初始化列表覆盖类内成员初始化的行为;

4.2 缺省构造函数

不需要提供实际参数就可以调用的构造函数

特性:

(1)如果类中没有提供任何构造函数,那么在条件允许的情况下,编译器会合成一个缺省构造函数

(2)合成的缺省构造函数会使用缺省初始化来初始化其数据成员

(3)调用缺省构造函数时避免 most vexing parse,Str m()会被理解为函数声明

(4)使用 default 关键字定义缺省构造函数,C++11后引入的,Str()=default;

4.3 单一参数构造函数

可以视为一种类型转换函数,Str m = 3;

可以使用explicit关键字避免求值过程中的隐式转换,explicit——显式的

4.4 拷贝构造函数

Str(const Str& x)

接收一个当前类对象的构造函数

特性:

(1)会在涉及到拷贝初始化的场景被调用,如参数传递。因此要注意拷贝构造函数的形参类型,使用引用类型避免递归

(2)如果未显式提供,那么编译器会自动合成一个,合成的版本会依次对每个数据成员调用拷贝构造

4.5移动构造函数

Str(Str&& x)

从C++11起,Str m2=std::move(m1);

接收一个当前类右值引用对象的构造函数

特性:

(1)可以从输入对象中 “ 偷窃 ” 资源,只要确保传入对象处于合法状态即可

(2)当某些特殊成员函数(如拷贝构造)未定义时,编译器可以合成一个

(3)通常声明为不可抛出异常的函数

(4)注意右值引用对象用做表达式时是左值!

4.6拷贝赋值与移动赋值函数

拷贝赋值运算符:Str& operator= (const Str& x){return *this;}

特性:

(1)注意赋值函数不能使用初始化列表

(2)通常来说返回当前类型的引用

(3)注意处理自身赋值的情况

(4)在一些情况下编译器会自动合成

移动赋值运算符:Str& operator=(Str&& x){val = std::move(x.val); return *this;}

4.7析构函数

特性:

(1)函数名:“~”加当前类型,无参数,无返回值

(2)用于释放资源

(3)注意内存回收是在调用完析构函数时才进行

(4)除非显式声明,否则编译器会自动合成一个,其内部逻辑为平凡的也就是啥也没干

(5)析构函数通常不能抛出异常

通常来说:(以含指针的类为例)

(1)如果需要定义析构函数,那么也需要定义拷贝构造与拷贝赋值函数,因为需要涉及到资源的拷贝

(2)如果需要定义拷贝构造函数,那么也需要定义拷贝赋值函数

(3)如果需要定义拷贝构造与赋值函数,那么也要考虑定义移动构造/赋值函数

default关键字:略

delete关键字:略

 

 

五.字面值类、成员指针与bind交互

5.1 字面值类

可以构造编译器常量的类型

特性:

(1)其数据成员需要时字面值类,如int可以,string不可以

(2)提供constexpr、consteval构造函数

(3)平凡的析构函数

(4)提供constexpr、consteval成员函数

5.2 成员指针

数据成员指针:int Str::*,该指针可以指向Str域中的一个数据成员

成员函数指针:int (Str::*)(double),该指针可以指向Str域中的一个输入double返回int的成员函数

成员指针对象赋值:auto ptr = &A::x;//A::x外面不能加小括号

posted @ 2023-03-02 20:11  啊原来是这样呀  阅读(21)  评论(0)    收藏  举报