重载类型转换
前言operator type() const类型转换只能重载为类的成员函数类型转换没有返回值,它的返回值由类型转换的类型来决定
正文昨天搞了字符串加数字合并成字符串
123hstring str{ "123abc" };str + -321;std::cout << str;
今天要逆转一下
1int x = (int)str;
类似于这种操作;
12345operator int();hstring::operator int(){}
看起来比较奇葩,而且虽然你没指定返回类型,但是编译的时候还是要你有一个返回值。
之前将数字转换成char类型通过ASCII码值去实现就是字符0的十进制值为48然后我们把数字+48转换成字符。
那么逆转之后,字符-48是否能为数字呢。
实践得真知~
这个原理没问题,但是要如何把这个数字转成长串,就是比如字符串"12345"你转换成数字,就是一万两千三百四十五。
总不能1 * 1000 + 2 * 1000 + 3 * 100 + 4 * 10 + 5这 ...
operator拓展
前言优化一下之前重载的时候 +就是让hstring + 1234也能实现,不过要对后面的数进行转换,且在不使用三方函数。
正文正常的一串数字123456跟char之间的联系,其实就要看ASCII码了。
可以看到字符0的二进制数是48,其它都是在48基础上+那么转换的思路就明确了。
12int a = 12345;char str[12]{}; //int 的范围,数了下大概长度10,然后算上符号。
再然后肯定要通过循环,for和while其实都差不多,但是都有一个痛点
123456while (a!=0){ int i = 0; str[i] = (char)(a % 10 + 48); i++; a %= 10;}
一开始傻瓜式写法,都不知道自己怎么想的。。。。
int i=0;放在循环里面,每次都是从0开始覆盖,没有意义
不一定要强转,编译器有隐转
while的条件倒也惨白,里面a%=10,如果最后一位是1,就死循环了。
所以有的时候脑子真的晕乎乎,修改一下
12345i ...
重载()
前言返回类型 operator()(类型 操作数);看到这个圆括号或多或少可以猜到跟函数有关()跟[]一样都只能重载为类的方法
()重载称为functor函数对象
不限制参数个数
可以做默认实参
利用()可以实现二维数组的访问方式
正文1const char& operator()();
123const char &hstring::operator()(){ return c_str[0];}
先随便玩一玩
看到当调用str()的时候不是出现其它函数,而是我们使用的重载,返回第一个元素虽然有点抽象。
但是要实现个稍微有用点的功能就要构思一下,感觉截取会比较直接。
1hstring operator()(const unsigned short _index, const unsigned short _length);
传递两个参数,一个起始位置,一个要截取的长度。
那么在设计的时候其实要考虑几个问题
字符串末尾0怎么处理
长度溢出是报错还是怎么处理
起始位置搞事情怎么办
123456789hstring hstring: ...
重载[]
前言[]在数组中比较常用,因为是取下标查看用的。
正文但是[]重载只能作为类的方法返回类型 operator[](类型 操作数);
拿我们自己写的hstring来说,目前就不能通过[]访问元素
提示没有匹配的操作数。
123char &hstring::operator[](const unsigned short _index) const{ return this->c_str[_index];}
好似效果实现了。
另外,我们返回的是一个引用,当然指针也行,所以我们能够修改它
这也是数组那会的基本效果。
缺点是不能对中文有很好的支持,因为不能保证字符编码。
然后就是越界行为,目前是未定义的。规范一下也不难,if判断一下。
12345678char &hstring::operator[](const unsigned short _index) const{ if (_index > hsLen){ return NULL; } else{ ...
重载<<和>>
前言在c++里 std::cout << 和 std::cin >>用多了可能都要忘了<<>>本质上是位移运算符。只不过std本质上是一个命名空间,可能在里面也定义了类也带上了重载。
正文12345//方法返回类型 operator>>(类型 操作数);//全局函数返回类型 operator>>(类型 左操作数,类型 右操作数);
这里还是以昨天hstring为例
12345678910111213141516171819//hstring.h#pragma onceclass hstring{private: char *c_str; //hstring => char *c_str unsigned short hsLen; //c_str length unsigned short hsmLen; //hstring 内存长度 unsigned short gethsLen(const char *str) const; ...
赋值运算符重载
前言在类的几个默认构造函数中,有一个拷贝用的,也叫副本构造。
12345class Box{public: int x; int y;};
默认的副本构造形式大概就是Box(const Box& box): x{ box.x }, y{ box.y }{ }这也是为什么初始化的时候,Box b2(b1)是合法的。
1234567int main(){ Box b1, b2; b1.x = 10; b1.y = 20; b2 = b1;}
而 b2 = b1 本质上就是因为运算符重载可以看到
正文自己手写这个赋值运算符重载也不难
1234567891011121314class Box{public: int x; int y; Box(){} Box(const Box &box) : x{ box.x }, y{ box.y }{} Box& ...
运算符重载
前言重载不陌生
123456int add(int a){ return ++a;}int add(int a,int b){ return a + b;}
函数重载就是一种。
正文运算符重载就是利用常见的运算符+-*/去做一些重载的事情。
比如string就可以实现+=
12string str = "hello ";str += "world";
默认的+肯定是只能对数值类型进行数学运算,而string这种重新设计的+就是运算符重载
语法:返回值类型 operator运算符();
自定义一个类
12345class Box{public: int height; Box(int h):height{h} {}};
在正常情况下,比较两个实例化对象的height属性
123456Box b1(100);Box b2(150);if(b1 < b2){ ... //肯定是不行的 ...
自定义类的函数调用约定
前言基础的类,默认调用成员函数使用的调用约定是_thiscall。
正文先随便写一段代码:
1234567891011121314151617#include<iostream>class Box{ int x;public: void Add(int a){ x += a; std::cout << x << std::endl; }};int main(){ Box b1; b1.Add(10); return 0;}
然后反汇编看调用Add的时候
12345 Box b1; b1.Add(10);002119AF 6A 0A push 0Ah 002119B1 8D 4D F4 lea ecx,[b1] 002119B4 E8 8C F7 FF FF call Box::Add (0211145h)
像这种调用ecx寄存器存放实例化对象的指针的调用约定就是_ ...
换个角度看类
前言逆向水的一批,顶多反汇编看看有啥过程
正文x64虽然快点,但是老的还是从x86开始
类的函数调用约定this1234567891011121314151617#include<iostream>class Box{ int hp;public: int Add(int a, int b){ return hp + a + b; }};int main(){ Box b1; b1.Add(100, 200); return 0;}
模式切换成release,选择x86
关闭优化
禁用安全检查
安全检查会增加不少的汇编代码,不利于常规分析。
然后随便打断点反汇编查看
123456789101112131415161718int main(){003A1020 55 push ebp 003A1021 8B EC mov ebp,esp 003A1023 51 ...
malloc和new
前言类的几个基本点差不多了,后续还有重载多态…
正文当然不是配套就行,还要知道为什么配套。
malloc和new对于基本数据类型,malloc和new没啥区别。但是对于类而言,malloc是仅分配内存,而new除了分配内存还会调用它的构造函数。
123456789101112class Box{ int count; static int s_count;public: Box(){ std::cout << "第" << ++s_count << "个构造函数" << std::endl; } ~Box(){ std::cout << "析构函数" << std::endl; }};
然后malloc分配看看会不会调用构造函数Box *b1 = (Box *)malloc(sizeof(Box) * 10);
看到什么都没有发生。
1234567891011121314class ...