类和对象

类的封装

1

private是私有的,外面看不见,要访问需使用外部接口

类定义的语法形式

1
2
3
4
5
6
7
8
9
class 类名{
public:
公有成员(一般把外部接口写这里)
类与外部的接口
private:
私有成员
protected:
保护型成员
}

对象定义的语法

1
clock myclock

成员访问

类内成员可互相访问

类外成员通过对象访问

成员函数的使用语法

1
2
3
4
5
void clock::settime(int newhour)
{
hour=newhour;
}
::代表该函数时clock类内的函数

内联成员函数

为了提高运行效率,通常把简单函数写城内联

语法

1.直接在类内写函数体

2.使用inline关键字

构造函数

目的

初始化对象

形式

函数名和类名相同

不能定义返回值类型,也不能有return语句

调用时机

对象被创建时自动调用

默认构造函数

1.参数表为空

1
clock()

2.参数表都是默认参数

1
clock(sethour=0)

隐含生成构造函数

当未写构造函数时,系统自动生成

例子

1
2
3
4
5
6
7
8
9
10
11
class clock
{
public:clock(sethour);
private:
int hour;
}
clock:clock(sethour):hour(int sethour){
}
int main(){
clock time1(0);
}

若我要初始化如下形式:

1
clock time1();

我需要在类中添加

1
clock();

=default

代码为:

1
clock();

无论初始化用不用得着,都要生成一个默认构造函数。

而使用default

1
clock() =default;

可以在只有用他的时候生成,提升效率

委托构造函数

当使用多个构造函数时,为避免代码重复,可以使用委托构造函数。

1
2
clock::clock:hour(setH){}
clock::clock:hour(0){}

这是普通情况

1
2
clock::clock:hour(setH){}
clock::clock:clock(0){}

这是使用委托构造函数,委托clock(a)

复制构造函数

如果不写,系统将自动生成一个默认的复制构造函数。

将一个已存在的对象去初始化一个新的对象

类名(const 类名 &对象名);

类名::类(const 类名 &对象名) {函数体}

如果不想去使用复制构造函数,可以用以下代码

1
point(const point &p) =delete;

2

右值与左值

左值是地址,右值是常数或式子。

析构函数

完成对象被删除前的一些清理工作。

语法:

1
~point();
1
point::~point(){}

类的组合

构造函数设计

不仅对本类初始化,也对对象成员初始化。

初始化次序是定义次序。

前向引用声明

如果要在类被定义前使用该类,可使用前向引用声明

1
class b;

再提供完整类之前,不能对该类对象进行声明,不能在内联成员函数中使用该对象。

数据的共享与保护

作用域

定义

作用域是一个标识符在程序正文中有效的区域。

分类

函数原型作用域

函数原型的参数,其作用域是“()”

如:

1
double area(double radius);

(double radius)是参数radius的作用域

局部作用域

函数的形参,在块中声明的标识符;

其作用域自声明处起,限于块中。

类作用域

类的成员具有类作用域,其范围包括类体和非内联成员函数的函数体。

命名空间作用域

命名空间可以解决类名,函数名等的命名冲突。

如何声明:

1
2
3
namespace 命名空间名{
各种声明
}
1
2
3
namespace somens{
class someclass{ };
}

引用类名:

1
somens::someclass obj1;

using语句的两种形式

1.using 命名空间名::标示符名;

可以让该标识符不需要加前缀。

2.using namespace 命名空间名;

该形式会让命名空间全部打开,不需要在写前缀 …::

特殊的命名空间:

1.全局命名空间:默认的命名空间

2.匿名命名空间:对每个源文件是唯一的

形式:

namespace后面不要跟名字

限定作用域的枚举类

1
2
3
4
5
enum color{red,yellow,green};//不限定作用域
enum color2{red,yellow,green};//错误,重复定义
enum class color2{red,yellow,green};//正确,限定作用域
color c = red;//全局作用域
color c2 = color2::red;//限定作用域

可见性

可见性表示从内层作用域向外层作用域看能看见什么

一个全局变量和局部变量重名,如果要在局部作用域中使用文件作用域的该全局变量,要使用::i。

静态变量不用初始化,他的初始值是0。若要初始化只能初始化一次。

生存期

对象从产生到结束的期间称为生存期

静态生存期

与程序运行期相同,在函数内部声明要加static

动态生存期

开始于声明处,结束于作用域结束时。

类的静态成员

静态数据成员

在类内声明

1
static int count;

定义和初始化

1
int point(类名)::count=0;

任何对象都能访问静态数据成员,并且是共享且唯一的。

静态函数成员

语法:

类内声明

1
static 返回值类型 函数名称(){};

类外使用

1
类名::函数名();

静态函数成员可直接访问静态数据成员,但不可以直接访问非静态数据成员。

类的友元

将一个模块视为另一个模块的朋友,一个模块可以访问另一个模块中被隐藏的信息

友元函数

在类声明中用friend做关键字修饰的非成员函数,可以访问private和protected成员

1
friend float list(point &a);
1
float list (point &a){}

因为友元函数时非成员函数,所以它在声明时可以放在类的任何一个位置。

要把对象作为函数形参,尽量使用引用,因为占用内存少。

友元类

3

共享数据的保护

常对象

常对象必须进行初始化而且不能被更新。

常成员语法

1
const 类名 对象名 = 数据;

用const修饰的类成员

常成员函数

1
返回值类型 函数名() const
1
返回值类型 函数名() const{}

常数据成员

1
2
const int a;
static const int b;
1
const int 类名::b = 10;

常引用

之前说过函数形参是对象时,尽量用引用,但是引用作为形参更改时,实参也会更改,为防止这件事,可以用常饮用。

1
void a(const point &a){}

多文件结构和编译预处理命令

c++程序的一般组织结构

包括:
1.类定义文件

2.类的实现文件

3.类的使用文件

外部变量和外部函数

外部变量

声明:在函数外部声明的变量

在其他文件中使用要在声明前加extern

外部函数

将变量和函数限制在编译单元内

一个文件下的函数和变量尽量不要让另一个文件访问,可采用匿名命名空间。

1
2
3
4
5
6
namespace {
int n;
void f(){
n++;
}
}

应当将不希望被其他编译单元看见的变量和函数放在匿名命名空间内。

编译预处理

#include

#define和#undef

条件编译指令

defined标识符

数组指针字符串

对象指针

指向类的非静态成员的指针

先声明,在赋值,最后引用

声明:

1
2
3
类型说明符 类名::*指针名
类型说明符 (类名::*指针名)(参数表);
类型说明符 (类名::*指针名)(参数表) const

赋值:

1
2
指针名=&类名::数据成员名
指针名=&类名::函数成员名

引用:

1
2
3
4
5
6
7
对象名.*类成员指针名
//或
对象指针名->*类成员指针名

(对象名.*类成员指针名)(参数表)
//或
(对象指针名->类成员指针名)(参数表)

指向类的静态成员的指针

可直接访问

1
2
类型说明符 *指针名 = &类名::数据成员;
类型说明符 (*指针名)(参数表) = &类名::函数成员;

动态内存分配

1
2
new 类型名T(初始化参数);
new 类型名T[表达式][常量表达式]...()

基本类型初始化:初始化参数这里,如果不写括号,初始值就是不确定的。如果只写了个括号。初始值为0.

对象初始化:不写括号和只写括号,使用默认构造函数初始化,写数值,用构造函数初始化,并且数值为实参。

1
char *fp[3];

这是指向一维数组的指针,一般用于动态内存分配。

释放内存表达符:delete

delete 指针名;

delete []指针名;释放指针p指向的数组。

使用delete时,会调用析构函数,来处理一些善后工作。

深层复制和浅层复制

浅层复制

是默认复制函数,复制值,地址不变。

深层复制

开辟新地址

1
2
3
DeepExample(const DeepExample& other) {
data = new int(*other.data); // 分配新内存并复制值
}