左值与右值
左值与右值在前几次的博客里,已经或多或少的在跟小伙伴们聊到这个点了。今天,咱们就以一篇文章,来专门的聊下C++中的左值与右值是怎么回事好啦!显然,我们可以望文生义,一个最简单粗暴的理解便是表达式等号左边的为左值,反之则为右值。这样的划分能够得到一个确切且合理的答案嘛?答案显然是不行的,在某些复杂情况下,左值与右值的判断可能是违反直觉的。 12345678910111213141516171819int x = 1;int get_val(){ return x;}void set_val(int val){ x = val;}int main() { x++; ++x; int y = get_val(); set_val(6);} 首先,这份代码第一个值得聊到的部分,就是x++与++x,虽然均是起到了自增的作用,但在左值和右值的划分上二者却是不同的 这里,为了简单明了起见,直接出答案x++为左值,++x为右值 分析如下 x++是先生成临时的复制后,随后进行递增,最后返回复制的内容 ++x则是对x递增后,返回自 ...
C++强制类型转换
C中的类型转换 今天提到的这个话题,想必各位在实际使用的过程中定然会时常用到。不过,一般来说,当发生类型转换时,我们一般的处理手段,通常是C风格的这种方案12345// 这种称之为隐式的类型转换,编译器默默地将int类型的变量进行了转换int a = 1;double b = a;// 或者说,这样,稍微显式一些的类型转换int c = (int)(5.0); ps:顺带一提,在进行类型转换时,建议进行由低精度向高精度的转换,如果方向相反的话,会造成精度丢失,数据误差C++中的类型转换 虽然上面的方法很简单粗暴,便于上手,但是在理解方面,可能就不是足够的清晰了,毕竟当某些隐式转换发生时,身为程序员可能会不易差距这一过程的发生。为了避免这一问题,除却类型转换这边,在构造函数中有个很鲜明的例子,那就是explicit来避免构造函数的隐式转换。不过,这不是今天所要聊到的主角了,为了适用于更多的场景,以及避免歧义,C++额外引入了四种新的转换方式主角1 static_cast 用途1:执行那些基本的内置数据类型转换 float data = static_cast<float>(1 ...
private、protected继承不可替代的理由
空基类内存优化技术C++类常为”空“,这就意味着在运行期其内部表示不耗费任何内存。这常见于只包含类型成员,非虚[成员函数]和静态数据成员的类,而非静态数据成员、虚函数和虚基类会在运行期耗费内存 空基类优化技术 当空类作为基类时,只要不会与同一类型的另一个对象或子对象分配在同一地址,****就不需要为其分配任何空间 为了保证不同的实例有不同的地址 1234567891011121314151617#include <iostream>class EmptyClass{};class EmptyToo : public EmptyClass{};class EmptyThree : public EmptyToo{};int main(){ printf("%lu\n", sizeof(EmptyClass)); // 1 printf("%lu\n", sizeof(EmptyToo)); // 1 printf("%l ...
python装饰器
python装饰器没错,今天聊到的话题是python的装饰器,自遇见这玩意的第一眼起,就对此十分的好奇(毫无理由的那种,实际上我也没怎么用过装饰器就是了) 首先,咱们以一段代码引入今天对于python装饰器的语法诠释,以便各位小伙伴们可以更好的理解。1234567891011def outWorld(): print("Hello World")@outWorlddef ourSolar(): print("Hello Solar")if __name__ == '__main__': ourSolar() 没错,我在此处做的工作就是写了两个简单无比的函数,然后用其中一个函数作为装饰器修饰另一个函数,说起来有些绕口,但聪明如你,一定会看出当前代码的所做之事。 那么,这个代码可以顺利运行嘛 答案是否定的。我们运行可以看到如下的报错outWorld() takes 0 positional arguments but 1 was given,编译器告知我们需要一个参数,那么对于这个函数,我们便简单粗暴地去遵循建 ...
浅谈枚举类型
浅谈enum enum,常见的枚举变量,想必大多对编程都有所认知的小伙伴,或多或少都会听闻过这个关键字 好了,一如既往的,让我们来讨论下enum的作用,以便我们后续可以更好的理解这种数据结构的必要性 作用 从定义上出发,enum可以理解为是由用户定义,具有一组意义的元素值 从实际出发,是为了做信息的标志和信息的分类 传统enum的局限性 作用域不受限,会引起命名冲突123456789#include <iostream>using namespace std;enum Color {RED, BLUE};enum Feeling {EXCITED, BLUD};int main(){ return 0;} 会隐式的转换为int 用来表征枚举变量的实际类型不能明确指定,从而无法支持枚举类型的前置声明 好吧,二三显然说的有些抽象,不过问题不大,我们接着往下聊 局限性的解决 作用域不受限,会引起命名冲突 一个常见思路,既然引起了命名冲突,我们就解决冲突,改名就好(显然有些许头秃) 作为C++程序员的我们,理 ...
浅谈仿函数
聊聊仿函数 仿函数是什么 类似函数,但并非函数的产物 从某种意义上来说,你可以理解了重载了运算符的类 仿函数的优点 更为的机智,能够拥有状态,这点主要是由于仿函数能够拥有自己的成员变量与成员函数 都具有其型别,一般函数,只要标记式不同便算型别不同 运行速度上,仿函数通常比函数指针更快,这点主要是由于采用模版所导致的特性,使得很多细节在编译期间就被决定了 作用 作为排序规则,在一些特殊情况下排序是不能直接使用运算符<或者>时,可以使用仿函数。 作为判别式使用,即返回值为bool类型。 同时拥有多种内部状态,比如返回一个值得同时并累加。 作为算法for_each的返回值使用。 实例 这个例子体现了仿函数的两个作用,在sort函数中的调用,以及作为相应判别式的作用 1234567891011121314151617181920212223class cmp{public: bool operator()(int a, int b) //从小到大 { return a < b; }}; bool cmp2(int a, ...
constexpr、可变模版、lambda用法拓展、fold表达式
浅谈const 聊到const,不可避免的会想到define,这通常是我们C++程序员定义所需常量的两种常见关键字 尽管在大多数情况下,二者无显著性的差异,但这并不代表他们毫无区别 其差异性简单总结如下 对比项目 define const 类型检查 不支持,单纯字符替换 支持,具有更好的安全性 常量定义 无限制 作为整型和枚举时,才可称之为常量表达式更多情况下,可理解为只读的语义 内存分配 内存中若干拷贝 内存中均有一份拷贝 汇编角度 返回一个立即数 给出一份地址 此外,const定义的变量,默认为局部变量。换言之,你没有用const定义的变量,潜在含有了extern关键字,可以被其他源文件调用。这一原因是在于const定义的变量只能在初始化定义一次,且定义后不能修改 熟悉的弯弯绕绕,指针常量与常量指针 区分方式:当const位于 * 左侧时,如const int *data,称之为常量指针;当const为 * 右侧时,如int * const data, 称之为指针常量 常量指针的特点 初始化时便要定义,无法分离者两个过程123456// 即这种写法会 ...
STL-Remove、判别式、lambda妙用
更易型算法(manipulating)copy妙用 快捷对容器元素进行分割 copy(data.begin(), data.end(), ostream_iterator<int>(cout, " ")); Remove妙用 remove的实际效果 remove(data.begin(), data.end(), 3) 理论上,想通过调用remove函数删除数据中所有的3,实际上的结果,只是将所有的3覆盖、末尾的3则不受影响 如若想实现真正的删除,写法应当如下,remove的结果实际上是返回迭代器的地址 data.erase(remove(data.begin(), data.end(), 3), data.end()) 返回实际删除元素的数量 distance(data.end(), remove(data.begin(), data.end(), 3)) for_each妙用 为data容器中的每个元素,调用print函数 for_each(data.begin(), data.end(), print) 类似的手法,可以通过transform ...
Bitmask
位运算why 位运算可以解决大量空间、提高编码效率 常见的数据类型int、char等均以字节为单位 当我们在特定情况下时,无需用过多的数据类型存储其数据 Bitmask 一个极为有效的写法 1 << index 可以通过此做法选定某个字节的指定位数来进行操作 ps:左移n位等价于乘以2^n 右移n位等价于除以2^n 实例1234567891011121314151617// 此处采用unit_8存储8栈灯的状态uint_8 lights;// 判断存储在此中灯的状态(此灯是否关闭)bool isLightoff (int index){ return (lights & (1<<index)) == 0;}// 改变灯的状态void changLightOn(uint_8& lights, int index, bool status){ if (status){ lights |= (1<<index); return; } l ...
C++_3_面向对象编程
面向对象编程的基本思想static扩充 类中的成员可分类为 数据成员 函数成员 其中,未被static修饰的均为非静态成员,需要通过实例化得以调用 换言之,非静态的成员属于对象,在实际调用过程中,是通过传入this指针,区分不同对象所调用的函数 静态成员则属于类,其函数在调用其参数列表中不会有this12345678910111213141516class Account{public: static double m_rate; static void set_rate(const double& x) { m_rate=x };}// 静态成员初始化double Account::m_rate = 8.0;int main(){ // 调用Static函数的方式有二 // 通过object Or class name Account::set_rate(5.0); Account a; a.set_rate(7.0);} 1234567891011template<t ...