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表达式步骤:

    1. 调用operator newoperator new[]的标准库函数,分配了一块足够大的未命名内存空间。
    2. 运行相应的构造函数,并传入初始值。
    3. 返回一个指向该对象的指针。
  • delete表达式步骤:
    1. 运行相应的析构函数。
    2. 调用operator deleteoperator 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表达式


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运算符

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限定符、链接指示相关推荐

  1. springmvc学习笔记(19)-RESTful支持

    springmvc学习笔记(19)-RESTful支持 标签: springmvc springmvc学习笔记19-RESTful支持 概念 REST的样例 controller REST方法的前端控 ...

  2. Python学习笔记19:列表 III

    Python学习笔记19:列表 III 其实这篇笔记标题应该是列表扩展,从列表开始,将涵盖Python中的序列容器. 关于列表的基础知识,可以看我的前两篇文章: Python学习笔记1:列表. Pyt ...

  3. Ext.Net学习笔记19:Ext.Net FormPanel 简单用法

    Ext.Net学习笔记19:Ext.Net FormPanel 简单用法 FormPanel是一个常用的控件,Ext.Net中的FormPanel控件同样具有非常丰富的功能,在接下来的笔记中我们将一起 ...

  4. 深度学习笔记(32) 目标定位

    深度学习笔记(32) 目标定位 1. 定位分类 2. 定位边框 3. 分类标签 4. 损失函数 1. 定位分类 图片分类任务已经熟悉了,就是算法遍历图片,判断其中的对象是不是汽车 定位分类问题,这意味 ...

  5. 区块链学习笔记19——ETH难度调整

    区块链学习笔记19--ETH难度调整 学习视频:北京大学肖臻老师<区块链技术与应用> 笔记参考:北京大学肖臻老师<区块链技术与应用>公开课系列笔记--目录导航页 前面学过,比特 ...

  6. Apollo学习笔记 进阶课程之三:定位技术②

    Apollo学习笔记 进阶课程之三:定位技术② 百度的无人驾驶定位方案 1).GNSS定位 GPS误差来源: 上图为单点定位,基于TOA 载波定位技术:(RPK技术,PPP技术) RPK:可以在五秒内 ...

  7. Linux 学习笔记19 信号

    Linux 学习笔记19 信号 信号 信号概述 为什么要是使用信号--为了实现进程的有序退出 信号是进程运行过程中,由自身产生或者由进程外部发来的消息.信号是硬件中断的软件模拟(软中断) signal ...

  8. C++学习之普通函数指针与成员函数指针

    http://blog.csdn.net/lisonglisonglisong/article/details/38353863 函数指针(function pointer)是通过指向函数的指针间接调 ...

  9. iOS学习笔记19 地图(一)定位CoreLocation

    ###一.定位介绍 现在很多社交.电商.团购应用都引入了地图和定位功能,似乎地图功能不再是地图应用和导航应用所特有的.的确,有了地图和定位功能确实让我们的生活更加丰富多彩,极大的改变了我们的生活方式. ...

最新文章

  1. 关于第十六届全国大学生智能汽车竞赛总决赛的规则建议
  2. MONGODB 集群 配置及 客户端PHP 连接
  3. best single model of RSNA
  4. 20万RMB!学数据分析的朋友一定要看!
  5. call,apply学习笔记
  6. GDI+ 应用,Release没有错误,Debug很多。
  7. 聊聊 Spring Boot 2.x 那些事儿
  8. 最困难的是带着自己的选择生活下去
  9. 云端服务器只能查看文件,云端服务器只能查看文件
  10. 冈萨雷斯--数字图像处理(MATLAB版)----书籍相关网站
  11. 操作系统学习(十三) 、中断和异常
  12. c#实现的破解程序--针对软件使用时间限制
  13. 前端class的中括号用法
  14. uni-app银行卡卡号验证
  15. html整体页面灰色,让网页整体变灰的方法
  16. 小小靖Java成长日记02
  17. Oracle19c数据库安装教程【Windows版】
  18. 或许你就是那个背锅侠【多图】
  19. Java 调用Python+Opencv实现图片定位
  20. 【转】一个女留学生在美国的七年

热门文章

  1. No qualifying bean of type xxx found for dependency expected at least 1 bean which qualifies as ...
  2. python从邮箱获取指定邮件_用python提取并统计指定邮箱里的特定邮件 | 学步园
  3. mac 系统怎么更改 pip 源
  4. Word2019建立自己的模板
  5. 成都百知教育:做Shopee店铺没有方向,这3大层级必须理清!
  6. arduino智能浇花系统_基于Arduino的远程自动浇花系统设计
  7. 商业模式笔记以及体悟
  8. 小米应用市场ASO关键词设置
  9. Vue组件——数字滚动抽奖效果
  10. 【论文阅读笔记】Multi-Task Feature Learning for Knowledge Graph Enhanced