学堂在线C++程序设计第八章学习笔记
指针与数组
定义指向数组元素的指针
定义与赋值
例如: int a[10], *pa;
pa = &a[0] 或 pa = &a
*(pa +1)就是 a[1]
也可以 pa[1]
指针数组:数组的元素是指针类型
例如:Point *pa[2];
由pa[0],pa[1]两个指针构成
指针与函数
为什么需要指针做参数
- 需要数据双向传递时(引用传递也可以达到此效果)
- 需要传递一组数据,只传首地址运行效率比较高
指针类型的函数
若函数的返回类型是指针类型,该函数就是指针类型的函数
指针函数的定义形式:
存储类型 数据类型 *函数名() {
}
- 不要将非静态局部地址用作函数的返回值
- 因为非静态局部地址
作用域
仅限于函数内部,离开函数就被销毁
了
- 因为非静态局部地址
int main() {
int *function();
int *ptr = function();
*ptr = 5; //危险!!!
return 0;
}
int *function() {
int local = 0; //作用域只在函数内部有效
return &local;
} //函数结束,变量local被释放
- 返回的指针要确保在主调函数中是
有效
,合法
的地址
在子函数中通过动态内存分配new
操作取得的内存地址返回给主函数是合法有效的,但是内存分配
和释放
不在同一级别,要注意不能忘记释放
,避免内存泄露
int main() {
int *function();
int *ptr = function();
*ptr = 5; //如果返回的是 new 分配的,就可以了
delete ptr; //但是这里需要释放,避免内存泄露
return 0;
}
int *function() {
int *local = new int(); //new 分配的不会离开函数就释放
return local;
}
指向函数的指针
函数指针的定义
- 定义形式
存储类型 数据类型 (*函数指针名)() - 含义
函数指针指向的是程序代码存储区
用途:函数回调
- 通过函数指针调用的函数
- 例如将函数的指针作为参赛传递给一个函数,使得在处理相似事件的时候可以灵活的使用不同方法
- 调用者不关心谁是被调用者
- 需要知道存在一个具有特定原型和限制条件的被调用函数。
对象指针
定义:
类名 *对象指针名;
例如:
Point a(5,10);
Point *ptr;
ptr = &a;
通过指针访问对象成员
对象指针名->成员名
例如:
ptr->getX() 相当于 (*ptr).getX()
this 指针
- 隐含于类的每一个非静态成员函数中
- 指出成员函数所操作的对象
- 当通过一个对象调用成员函数时,系统先将该对象的地址赋给this指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含的使用了this指针。
动态内存分配
动态申请内存
new 类型名T (初始化参数列表)
- 功能:
在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋值 - 结果值:
成功:T类型的指针,指向新分配的内存,失败:抛出异常
释放内存
delete 指针P
- 功能:
释放指针P所指向的内存,p必须是new操作符的返回值
智能指针
C++11提供的
- unique_ptr
- 不允许多个指针共享资源,可以用标准库中的move函数转移指针
- shared_ptr
- 多个指针共享资源
- weak_ptr
- 可复制 shared_ptr ,但其构造或者释放对资源不产生影响
Vector
为什么需要Vector
- 封装任何类型的动态数组,自动创建和删除
- 数组下标越界检查
定义
- vector<元素类型> 数组对象名(数组长度);
- 例子:
vector arr(5) 建立大小为5的int数组
vector对象的使用
- 对数组元素的引用
与普通数组具有相同形式:
vector对象名[下标表达式]
vector数组对象名不表示数组首地址 - 获得数组长度
用size
函数
vector对象名.size()
对象复制和移动
浅层复制
- 实现对象间数据元素的一一对应复制
深层复制
- 当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指对象进行复制
申请和释放动态数组
分配和释放动态数组
- 分配:new 类型名T [数组长度]
- 数组长度可以是任何整数类型表达式,在运行时计算
- 释放:delete[] 数组名p
- 释放指针p所指向的数组
- p必须是用new分配得到的数组首地址
动态创建多维数组
new 类型名T[第1维长度][第2维长度]
- 如果内存申请成功,new 运算返回一个指向新分配内存首地址的指针
- 例如
char (*fp)[3];
fp = new char[2][3]
将动态数组封装成类
- 更加简洁,便于管理
- 可以在访问数组元素前检查下标是否越界
移动构造
C++11中提供了新的构造方法
C++11之前,如果要将源对象的状态转移到目标对象只能通过复制。在某些情况下,我们没有必要复制对象 – 只需要移动它们
移动构造
- C++11引入移动语义:
- 源 对象资源的控制权全部交给目标对象
- 移动构造函数
移动构造函数
class_name(class_name &&)
字符串
C风格字符串
字符串常量
- 例如:“program”
- 各字符连续,顺序存放,每个字符占一个字节,以’\0’结尾,相当于一个隐含创建的字符常量数组
- “program” 出现在表达式中,表示这个char数组的首地址
- 首地址可以赋给char常量指针
- const char *STRING1 = “program”
用字符数组存储字符串
- 例如
char str[8] = {‘p’,‘r’,‘o’,‘g’,‘r’,‘a’,‘m’,’\0’}
char str[8] = “program” - 缺点
- 执行连接,拷贝,比较等操作,都需要显式调用库函数,很麻烦
- 当字符串长度不确定时,需要用new 动态创建字符数组,最后要用delete释放,很繁琐
- 字符串实际长度大于为他分配的空间时,会产生数组下标越界的错误
string类
构造函数
- string() 默认的,建立长度为0的字符串
例如 string s1; - string(const char *s); //用指针s所指向的字符串常量初始化string对象
例如 string s2 = “abc”; - string(const string& rhs); //复制构造函数
例如 string s3 = s2;