C++ 学习笔记(19)new/delete表达式、定位new、typeid、dynamic_cast、type_info、枚举类型、成员函数指针、union、位域、volatile限定符、链接指示
C++ 学习笔记(19)new/delete表达式、定位new、typeid、dynamic_cast、type_info、枚举类型、成员函数指针、union、位域、volatile限定符、链接指示
参考书籍:《C++ Primer 5th》
C++ 学习笔记(12)动态内存、智能指针、new和delete、动态数组、allocator
19.1 控制内存分配
19.1.1 重载new和delete
- new表达式步骤:
- 调用
operator new
或operator new[]
的标准库函数,分配了一块足够大的未命名内存空间。 - 运行相应的构造函数,并传入初始值。
- 返回一个指向该对象的指针。
- 调用
- delete表达式步骤:
- 运行相应的析构函数。
- 调用
operator delete
或operator delete[]
的标准库函数,释放内存空间。
重载带有noexcept说明符运算符时,同样需要加上noexcept。
自定义版本必须位于全局作用域或类作用域。
- 作为类的成员时,是隐式静态的,因为new是在对象创建前使用,delete是在对象销毁以后使用,且都不能操纵类的任何数据成员。
new的返回类型必须是
void *
,第一个形参类型必须是size_t
,且不能有默认实参(分配对象所需字节数)。可以提供额外形参,除了一个函数用户不能重载:void *operator new(size_t, void*);
delete的返回类型必须是
void
,第一个形参必须是void*
。用指向待释放内存的指针,来初始化void*
形参。nothrow_t:空结构体,用来表明函数是否抛出异常。
实际上并没有重载new表达式delete表达式,只是改变内存分配方式。因为无法自定义new表达式或delete表达式的行为。
malloc函数:
- 参数
size_t
:分配字节数。 - 返回
void*
:分配了空间的指针。失败返回0。
- 参数
free函数:
- 参数
void*
:是malloc返回的指针的副本,将其内存释放。
- 参数
19.1.2 定位new表达式
- place_address 必须是一个指针。
- 定位new调用
operator new(size_t, void *)
:函数不分配内存,只是返回指针实参;再由new表达式负责初始化对象。 - 不同于allocator的construct,定位new表达式的指针不需要指向动态内存。
- 和allocate的destroy类似,调用析构函数可以清除对象,但不会释放对象所在空间。
19.2 运行时类型识别
- 运行时类型识别(run-time type identification, RTTI):
- typeid运算符,用于返回表达式的类型。
- dynamic_cast运算符,用于基于类的指针或引用安全地转换成派生类的指针或引用。
- 使用RRTI时,最好定义虚函数而非直接管理类型。
19.2.1 dynamic_cast运算符
- 有三种形式(type为类类型,通常含有虚函数):
dynamic_cast<type*>(e)
:e必须为有效的指针。dynamic_cast<type&>(e)
:e必须为一个左值。dynamic_cast<type&&>(e)
:e不能是左值。
- e的类型必须符合以下三个条件中任意一个:
- 是目标type的公有派生类。
- 是目标type的公有基类。
- 是目标type的类型。
- 转换失败时:
- 目标是指针类型,返回0。
- 目标是引用类型,抛出bad_cast异常。
// 指针类型
// bp指针指向基类Base(至少含有一个虚函数),Derived是Base的公有派生类。
if (Derived *dp = dynamic_cast<Derived*>(bp))
{// 转换成功。dp指向Derived对象。
}
else
{// 转换失败。使用dp指向的Base对象
}// ----------------------------------------------------------------------------- //// 引用类型
// 因为不存在空引用,对于引用失败,应该用捕获异常的方法。
void f(const Base &b)
{try {// 使用b引用的Derived对象。const Derived *d = dynamic_cast<Derived&>(b);} catch (bad_cast) {// 转换失败处理。}
}
19.2.2 typeid运算符
typeid(e)
:e是任意表达式或类名。返回结果是type_info类型(或派生类)常量对象的引用。- e表达式如果是引用,则typeid返回该对象的类型。
- e表达式如果是数组或函数,不会转成指针,而是原来的类型。
- typeid作用指针时,返回的结果是该指针的静态编译时类型。
- 只有当类型含有虚函数时,编译器才会对表达式求值。
- 不含虚函数时,返回表达式的静态类型。
class A {}; // 非多态
class B : public A {};class A2 { virtual void foo() {}; }; // 多态
class B2 : public A2 {};void main()
{B *b = new B;A *a = b;if (typeid(*a) == typeid(*b)) { cout << "test one ok!" << endl; } // 未执行if (typeid(*a) == typeid(B)) { cout << "test two ok!" << endl; } // 未执行if (typeid(a) == typeid(B)) { cout << "test three ok!" << endl; } // 未执行B2 *b2 = new B2;A2 *a2 = b2;if (typeid(*a2) == typeid(*b2)) { cout << "test four ok!" << endl; } // 执行,类型相同if (typeid(*a2) == typeid(B2)) { cout << "test five ok!" << endl; } // 执行,类型相同if (typeid(a2) == typeid(B2)) { cout << "test six ok!" << endl; } // 未执行,比较的是:A2*和B2类
}
19.2.3 使用RTTI
class Base
{friend bool operator==(const Base&, const Base&); // 整个类(包括继承)的相等运算符
protected:virtual bool equal(const Base&) const; // 每个类自己定义的euqal函数
};class Derived : public Base
{
protected:bool equal(const Base&) const;
};// ==运算符的比较
bool operator==(const Base &lhs, const Base &rhs)
{return typeid(lhs) == typeid(rhs) && lhs.equal(rhs); // 先判断类型是否相同,再调用自定义的相等判断
}// 基类自定义判断
bool Base::equal(const Base &rhs) const
{// ...自定义的相等判断
}// 继承类自定义判断
bool Derived::equal(const Base &rhs) const
{auto r = dynamic_cast<const Derived&>(rhs); // 继承类转基类,不会抛出异常if (!Base::equal(rhs)) return false; // 可调用基类判断,先处理基类的成员判断// ...其他自定义的相等判断return true;
}
19.2.4 type_info 类
19.3 枚举类型
- 枚举属于字面值常量类型。
- 限定作用域的枚举类型:包含关键字class或struct,枚举类型名,枚举成员列表。
- 不限定作用域的枚举类型:省略关键字class或struct,枚举类型的名字是可选的。
// 不限定作用域的枚举类型
enum color { red, green };
enum stoplight { red, green }; // 错误。重复定义枚举成员
enum class peppers { red, green }; // 正确。限定作用域的枚举类型,隐藏了枚举成员color eyes = green; // 正确,不限定作用域的值
peppers p = green; // 错误,color枚举类型不能赋给peppers
peppers p2 = peppers::red; // 正确,指定作用域int i = color::red; // 正确,不限定作用域的枚举可隐式转换成int
int j = peppers::red; // 错误,限定作用域的不会隐式转换
int k = (int)peppers::red; // 可以强制转换
- 默认情况下:
- 限定作用域的enum成员类型是int。
- 不限定作用域的enum成员不存在默认类型。所以在前置声明时必须指定成员大小。
// 不限定作用域的枚举类型,必须指定成员类型
enum intValues : usigened long long;
// 限定作用域,默认int
enum class open_modes;
19.4 类成员指针
19.4.2 成员函数指针
class Screen
{
public:Screen & up();Screen& down();// Action是别名,Screen的成员指针,可指向Screen成员的一类函数。using Action = Screen & (Screen::*)();enum Directions { UP, DOWN }; // 用来控制输入的枚举类型Screen& move(Directions); // 提供给外部调用的接口
private:static Action Menu[]; // 函数列表,通过索引调用对应函数
};Screen::Action Screen::Menu[] = { &Screen::up, &Screen::down }; // 0:up 1:downScreen& Screen::move(Directions cm) { return (this->*Menu[cm])(); } // 通过函数表直接调用对应函数void main
{Screen s;s.move(Screen::DOWN); // 等价 s.down();
}
19.4.3 将成员函数用作可调用对象
- mem_fun函数:可以根据成员指针的类型推断可调用对象的类型。
auto fp = &string::empty; // 一般获取的成员函数都是指针类型,不能作为对象
find_if(sv.begin(), sv.end(), fp); // 错误。fp是成员指针,需要调用 ->*// 使用function转换成对象
function<bool (const string&)> fcn = &string::empty;
find_if(sv.begin(), sv.end(), fcn); // 正确。fcn是函数对象// 使用mem_fn
find_if(sv.begin(), sv.end(), mem_fn(&string::empty)); // 正确,直接生成可调用对象
auto f = mem_fn(&string::empty); // f可接受对象或对象的指针
f(*svec.begin()); // 正确,传入对象,f调用.*
f(&svec[0]); // 正确,传入指针,f调用->*// 使用bind,必须将函数指向对象的隐式形参转换成显式的。
auto fb = bind(&string::empty, _1);
fb(*svec.begin()); // 正确
fb(&svec[0]); // 正确
19.6 union:一种节省空间的类
union可以有多个数据成员,但在任意时刻只有一个数据成员有值(其他成员变成未定义状态)。分配union对象的存储空间至少能存储其最大的数据成员。
不能含有引用类型的成员。
默认情况下,成员都是公有的。
不能继承其他类或作为基类。
匿名union:未命名的union。定义了匿名union,编译器就自动为该union创建一个未命名对象。
- 定义所在作用域内,该union的成员都是可以直接访问的。
- 不能包含proteced或private成员,也不能定义成员函数。
union
{char cval;int ival;
};
cval = 'c'; // union对象的成员,此时ival是未定义状态
ival = 2; // 赋值了另一个成员,此时cval是未定义状态
- union只包含内置类型的成员时,编译器依次合成默认构造函数或拷贝控制成员。
- 如果含有类类型成员,且需要修改类类型成员时,就必须运行构造或析构函数。如果定义了自定义默认构造函数或拷贝成员,合成版本的会被声明为删除的。
19.7 局部类
- 如果局部类定义在某个函数内部,局部类无法使用函数内部的局部变量。
19.8 固有的不可移植的特性
- 不可移植(nonportable):因机器而异,通常需要重新编写。
19.8.1 位域
struct S
{unsigned int b : 3; // 三位无符号位域,值的范围在[0,7]
};
void main()
{S s = { 7 };cout << s.b << endl; // 输出:7++s.b; // 无符号上溢(保证回卷)cout << s.b << endl; // 输出:0
}
19.8.2 volatile限定符
volatile int v;
int *vloatile vip; // vip:volatile指针,指向int
vloatile int *ivp; // ivp:是一个指针,指向vloatile int
vloatile int *vloatile vivp; // vivp:volatile指针,指向vloatile intint *ip = &v; // 错误
ivp = &v; // 正确
vivp = &v; // 正确
19.8.3 链接指示:extern “C”
// 单语句链接指示:声明使用C语言版本的strlen函数
extern "C" size_t strlen(const char *);// 复合语句链接指示
extern "C"
{int strcmp(const char*, const char*);char *strcat(char*, const char*);
}extern "C"
{#include <string.h> // 操作C风格字符串的C函数
}void (*pf1)(int); // pf1指向一个C++函数
extern "C" void (*pf2)(int); // pf2指向一个C函数
pf1 = pf2; // 错误,C和C++是不同类型
// 在同时编译C和C++时,做判断以正确编译
#ifdef __cplusplus
extern "C"
#endif
extern "C" void print(const char *);
extern "C" void print(int); // 错误,相同函数名extern "C" double calc(double);
extern int clac(int); // 正确,C++与C函数的重载。
C++ 学习笔记(19)new/delete表达式、定位new、typeid、dynamic_cast、type_info、枚举类型、成员函数指针、union、位域、volatile限定符、链接指示相关推荐
- springmvc学习笔记(19)-RESTful支持
springmvc学习笔记(19)-RESTful支持 标签: springmvc springmvc学习笔记19-RESTful支持 概念 REST的样例 controller REST方法的前端控 ...
- Python学习笔记19:列表 III
Python学习笔记19:列表 III 其实这篇笔记标题应该是列表扩展,从列表开始,将涵盖Python中的序列容器. 关于列表的基础知识,可以看我的前两篇文章: Python学习笔记1:列表. Pyt ...
- Ext.Net学习笔记19:Ext.Net FormPanel 简单用法
Ext.Net学习笔记19:Ext.Net FormPanel 简单用法 FormPanel是一个常用的控件,Ext.Net中的FormPanel控件同样具有非常丰富的功能,在接下来的笔记中我们将一起 ...
- 深度学习笔记(32) 目标定位
深度学习笔记(32) 目标定位 1. 定位分类 2. 定位边框 3. 分类标签 4. 损失函数 1. 定位分类 图片分类任务已经熟悉了,就是算法遍历图片,判断其中的对象是不是汽车 定位分类问题,这意味 ...
- 区块链学习笔记19——ETH难度调整
区块链学习笔记19--ETH难度调整 学习视频:北京大学肖臻老师<区块链技术与应用> 笔记参考:北京大学肖臻老师<区块链技术与应用>公开课系列笔记--目录导航页 前面学过,比特 ...
- Apollo学习笔记 进阶课程之三:定位技术②
Apollo学习笔记 进阶课程之三:定位技术② 百度的无人驾驶定位方案 1).GNSS定位 GPS误差来源: 上图为单点定位,基于TOA 载波定位技术:(RPK技术,PPP技术) RPK:可以在五秒内 ...
- Linux 学习笔记19 信号
Linux 学习笔记19 信号 信号 信号概述 为什么要是使用信号--为了实现进程的有序退出 信号是进程运行过程中,由自身产生或者由进程外部发来的消息.信号是硬件中断的软件模拟(软中断) signal ...
- C++学习之普通函数指针与成员函数指针
http://blog.csdn.net/lisonglisonglisong/article/details/38353863 函数指针(function pointer)是通过指向函数的指针间接调 ...
- iOS学习笔记19 地图(一)定位CoreLocation
###一.定位介绍 现在很多社交.电商.团购应用都引入了地图和定位功能,似乎地图功能不再是地图应用和导航应用所特有的.的确,有了地图和定位功能确实让我们的生活更加丰富多彩,极大的改变了我们的生活方式. ...
最新文章
- 关于第十六届全国大学生智能汽车竞赛总决赛的规则建议
- MONGODB 集群 配置及 客户端PHP 连接
- best single model of RSNA
- 20万RMB!学数据分析的朋友一定要看!
- call,apply学习笔记
- GDI+ 应用,Release没有错误,Debug很多。
- 聊聊 Spring Boot 2.x 那些事儿
- 最困难的是带着自己的选择生活下去
- 云端服务器只能查看文件,云端服务器只能查看文件
- 冈萨雷斯--数字图像处理(MATLAB版)----书籍相关网站
- 操作系统学习(十三) 、中断和异常
- c#实现的破解程序--针对软件使用时间限制
- 前端class的中括号用法
- uni-app银行卡卡号验证
- html整体页面灰色,让网页整体变灰的方法
- 小小靖Java成长日记02
- Oracle19c数据库安装教程【Windows版】
- 或许你就是那个背锅侠【多图】
- Java 调用Python+Opencv实现图片定位
- 【转】一个女留学生在美国的七年
热门文章
- No qualifying bean of type xxx found for dependency expected at least 1 bean which qualifies as ...
- python从邮箱获取指定邮件_用python提取并统计指定邮箱里的特定邮件 | 学步园
- mac 系统怎么更改 pip 源
- Word2019建立自己的模板
- 成都百知教育:做Shopee店铺没有方向,这3大层级必须理清!
- arduino智能浇花系统_基于Arduino的远程自动浇花系统设计
- 商业模式笔记以及体悟
- 小米应用市场ASO关键词设置
- Vue组件——数字滚动抽奖效果
- 【论文阅读笔记】Multi-Task Feature Learning for Knowledge Graph Enhanced