C++相关概念和易错语法
文章平均质量分 90
涵盖C++几乎所有常用语法,持续更新
-SGlow-
这个作者很懒,什么都没留下…
展开
-
C++相关概念和易错语法(1)(命名空间域、缺省参数、函数重载)
但是在大型的项目中一般用局部展开,因为如果当同时展开多个命名空间时,根据访问规则,如果展开的这些命名空间有重名的情况,就会报错。调用函数时,函数地址是执行第一句汇编指令的地址,要有定义才有这个地址。但是区别在于函数里的变量只有在进入函数后在函数里面访问,而namespace定义的变量可以在任何地方调用(生命周期所致)。一般情况下,命名空间域不会被访问,在使用using被展开后会在先局部再全局访问后,访问这个命名空间。时就会先访问局部域,在任何函数里面定义的变量,你在函数外面都访问不了,因为。原创 2024-03-30 17:47:19 · 754 阅读 · 1 评论 -
C++相关概念和易错语法(2)(引用、内联函数、auto类型)
因为如果一个函数有成百上千代码,而这个函数被数百次调用,那么在预处理阶段被展开的代码量是极为恐怖的,所以为了防止这种事情发生,引用作为一种对变量取别名的用法,对别名和本体的访问权限必须一致,有的情况下比较容易出错。但引用的安全并不是绝对的,如果引用的指针存在越界的情况,语法层面也无法检测出来。这相当于对数组里面的内容取别名,再在后面的代码中对x操作就可以影响数组的值。注意甄别权限的放大,有的情况下只是赋值操作,不存在放大的情况。,就当成正常函数使用,这个时候的开支也一定是最合理的。原创 2024-04-02 23:48:41 · 329 阅读 · 5 评论 -
C++相关概念和易错语法(3)(类的声明和定义、空指针分析、this指针)
但是,在成员函数内部,可以显式写出this指针,因为this指针其实是类的指针,而类又和struct同源,所以用的是this->形式。this指针可能存在栈中,也可能存在寄存器中,不同编译器有不同的做法。它是隐含在类中的一种指针,在对该类实例化出多个对象时,this指针就用来给每个对象贴上标签。这些成员变量、函数的作用于这个类域,将功能集成在一起,这体现出封装的思想。为形象理解,可以将类的声明视为图纸,这个图纸可以实例出多个对象。预处理是将所有的宏和头文件展开,生成的文件我们仍然能读懂。原创 2024-04-05 13:02:47 · 686 阅读 · 1 评论 -
C++相关概念和易错语法(4)(构造函数、析构函数)
解释:c3没有自定义构造函数,所以自动生成构造函数。_p和_q都是内置类型所以不会初始化,_r是自定义类型,所以调用_r的自定义构造函数。_r有自定义构造函数,将_x和_y都初始化为2,_z是自定义类型,所以调用_z的自定义构造函数。需要注意的是,析构函数不能带有参数,包括全缺省,所以也不需要考虑传参的各种情况。我们发现,_x和_y并没有初始化,而_a和_b初始化了,如何解释?_z有自定义构造函数,将_a和_b都初始化为2。因此,在我们平时自定义构造函数的时候,最好使用。原创 2024-04-05 13:03:49 · 994 阅读 · 1 评论 -
C++相关概念和易错语法(5)(析构函数、拷贝构造、运算符重载、赋值重载)
内置类型的变量是自动变量,是编译器自动开辟的,不需要人为销毁,因此也不需要写析构函数。没有资源管理(堆空间开辟)的情况可以不写拷贝构造,内部有指针或有一些值指向堆,需要显式写析构函数,也要写拷贝构造,如栈、队列、二叉树等数据结构。在这里可以看出,在C这个类中的函数可以访问调用该函数的对象的私有成员,也可访问同类型的参数对应的私有成员。要解决这个无穷递归的情况,可以选择传址或传引用,因为这两者本质相同,而传引用不用显式地写&,从而实现C c2 = c1这样方便的写法,所以规定拷贝构造都是传引用。原创 2024-04-18 18:40:14 · 694 阅读 · 0 评论 -
C++相关概念和易错语法(6)(运算符重载)
由上面我们就可以知道为什么C++相比较C要多设计cin和cout了,其主要目的是为了适配自定义类型的输入输出,这在C语言中的printf和scanf实现起来非常困难,需要借助函数来实现。因此,在学习C++的过程中,我们要仔细思考为什么C++要设计一个类似的语法,C语言一定存在相对应的局限性。69.默认成员函数:取地址的运算符重载,这个函数也可以用const修饰,我们不写也可以,一般而言不自己写。如果所有的函数都在.h定义,有两个或以上源文件包含这个头文件的时候,这份定义会分别出现在两个源文件里,原创 2024-04-20 15:17:56 · 291 阅读 · 1 评论 -
C++相关概念和易错语法(7)(初始化列表、隐式类型转换、友元)
假设我们在成员变量声明处写了int _a = 1,如果我们没有自己写初始化列表,而是在函数体内写了_a = 2,那么当_a创建时会自动创建一个初始化列表,其中_a初始化时在初始化列表中赋的值就是缺省值,也就是_a(1),后续再进入函数体,将_a赋值为2。如果我们显式实现了初始化列表,如在初始化列表中写了_a(1),在成员变量声明处写了缺省值int _a = 2,这个时候在编译器创建变量并初始化时就会。就在上面的那段代码,我们会注意到A的_a和_b是共有的,如果是私有的,那么如何处理呢?原创 2024-04-21 00:03:52 · 865 阅读 · 0 评论 -
C++相关概念和易错语法(8)(匿名对象、构造+拷贝构造优化、构造析构顺序)
当我们实例化对象后,有的对象可能只使用一次,之后就没用了。我们可以发现,匿名对象的生命周期就在那一行,在调用之后就会立马销毁。(4)除了static修饰的局部对象以外,所有对象(局部、全局)先构造的后析构。对于优化问题,我们常常讨论的是构造次数,因为析构和构造成对出现。(2)局部static对象在第一次调用才构造,第二次就不构造了。一般来说采用后者的写法,毕竟要写的代码量更小,而且提高可读性。(1)全局对象最先构造,在程序开始时就按顺序构造。,完成匿名对象的初始化;但是,有的情况并不会优化,主要是看。原创 2024-04-21 17:37:09 · 1015 阅读 · 1 评论 -
C++相关概念和易错语法(9)(变量的存储、new和delete混用分析)
我们发现如果这个时候直接去调用operator delete[]、operator delete、delete、free就会导致释放的内存起始位置发生了位移,这就会导致报错,而delete[]就会先从前4个字节开始,先读取个数,然后。注意数组是开辟了空间的,会发生复制操作,就算是常量字符串储存在代码段,也要拷贝过来。如果类没有显式实现析构函数,那么就意味着不会多开辟那4个字节,也就不会报错,这个时候混用不会导致程序崩溃。当我们创建类的数组时,使用new[],因为使用它可以在开辟空间的时候。原创 2024-04-28 21:34:52 · 852 阅读 · 2 评论 -
C++相关概念和易错语法(10)(定位new、模板)
当我们向内存池申请空间,使用malloc时无法直接调用构造函数,我们需要主动去调用构造函数,那应该怎么办呢?定位new的使用场景很少,如果我们想将一个已使用对象初始化,我们就可以使用这种办法,同时,它在内存池(池化技术)中使用比较多,,我们只有多依靠复制粘贴实现多个极为相似的函数,这样会使代码变得冗长,而且使得效率低下,因此C++中引入了模板的概念,注意格式中new后面的括号内的是指针,之后跟初始化信息,单参数用小括号,多参数要用花括号。如果普通函数的参数不能匹配,就会去匹配更符合的函数模板,原创 2024-05-01 11:51:03 · 1034 阅读 · 1 评论 -
C++相关概念和易错语法(11)(npos、string的基本使用)
但由于string的设计相对来说很复杂(string设计在STL之前,时间太早不太成熟,设计出现了很多冗余的接口),平时使用的时候就主要使用关键的几个,保证功能的实现即可,所以这篇文章会分享一些基础的有用的接口。用来查询字符串允许的最大长度,几乎没什么用,因为一般情况不会触及到这个最大值,下面是在x64下允许存储的最大长度,相当于有符号long int能存的最大值,需要8589934592G的空间,这是不可能达到的。但是我们要注意的是这里面有很多的构造函数,在使用的时候是去找最匹配的。原创 2024-05-05 08:51:19 · 902 阅读 · 3 评论 -
C++相关概念和易错语法(12)(迭代器、string容量调整)
这个区别和指针的const规则类似,只不过指针是根据const和*的相对位置来区分的,迭代器iterator就一个单词,不存在相对位置的概念,所以使用const_放在iterator前面表示区分。,虽然我们知道\0也是字符串的一部分,本质上说也不算越界,但是考虑到实际使用没正常人会去对这个\0动手脚(如果单独改了这个\0,就会导致字符串没有\0,会引发一系列隐患),我们发现扩容后,size直接跳到100,意味着0~99的数据都能被正常访问,在默认情况下,扩容后的空位补\0,你也可以指定字符。原创 2024-05-07 11:42:42 · 741 阅读 · 1 评论 -
C++相关概念和易错语法(13)(string的模拟实现)
借助复用我们可以进一步实现erase,当从pos开始全部删除时用swap,其余情况分成两段insert,最后加起来,这里我提前用了operator+,operator+很好实现,只是我这里讲解的顺序不一样。当存储字符少于15个的时候(实际开了16个字节,预留一个给\0),就存到_buff里,多了就全部移到_str中。比较运算符重载我们其实只需要实现其中的一两个,其它全部用上层逻辑联系起来,可以很快实现。利用复用,我们的代码可以非常简约,但是实际上执行的复杂度是没有改变的,大部分的代码是很简单的,原创 2024-05-20 18:06:45 · 494 阅读 · 2 评论 -
C++相关概念和易错语法(14)(初始化注意事项、vector、编译器向上查找规则)
string可以一次性加入一个串,而vector只能单独加字符,string不管怎么操作,数据最后一定有个\0,而vector尾插不会有这个功能,需要自己去处理\0的情况,很不方便。图1展开命名空间的语句在展开头文件的下面,因此在向上查找的情况下,并不会读取using namespace std,也就不会在std命名空间里找,所以找不到。因此我们可知,string是专门针对字符数组的需求设计的,而vector是广泛适应的,vector不能替代string,需要根据不同的场景有不同的选择。原创 2024-05-24 08:58:30 · 954 阅读 · 0 评论 -
C++相关概念和易错语法(15)(sort、vector模拟实现)
因此它的本质是vector支持一种构造,auto il = {1,2,3,4,5}将任意个数的数据(常量数组)给initializer_list,这个对象里有两个指针,分别指向开始和结束的下一个位置,它也支持迭代器,范围for。当开辟空间后,_start首位置,_finish存储位置的下一个位置,_end_of_storage都更新了,pos迭代器也要去更新,_start首位置,_finish存储位置的下一个位置,_end_of_storage开辟空间的最后一个下标下一个。其余接口都算简单,就不再介绍了。原创 2024-06-03 19:11:15 · 921 阅读 · 0 评论 -
C++相关概念和易错语法(16)(list)
_data是结构体类型,我们直接先指向这个结构体内容本身,即_curnode->_data,这个表达式的返回值是一个结构体,也就是我们想要访问的结构体,但是我们是要模拟指针的操作,要使用->而不是.,所以我们要再对它取地址,即&(_curnode->_data),这样返回的就是一个指向_data的指针了,当我们。当我们调用insert(lt.begin(), 1, 3)时,是想在首位置插入1个3,但是编译器会将1,3识别成迭代器(注意看我们之前已经实现了模板函数,1和3同类型是会匹配的),原创 2024-07-08 11:17:12 · 976 阅读 · 0 评论 -
C++相关概念和易错语法(17)(适配器模式、仿函数)
在queue中pop使用了pop.front(),在vector里没有这个接口,所以不能使用vector实现queue,但是我们如果用erase来实现pop也可以,这样就可以用vector实现queue了,但并不建议这么做,因为vector实现queue本就是效率极低的做法。构造时我们可能空构造,需要缺省值,也有可能带参构造,带参构造需要我们形参接收,这里展示的是拷贝构造的情况,析构的时候成员_con作为自定义类型会去调用自己的析构函数,不会存在内存泄露的情况,所以不需要我们显式的去写析构。原创 2024-07-09 20:52:25 · 1192 阅读 · 0 评论 -
C++相关概念和易错语法(18)(array、模板)
对于普通函数,就算我们不去调用它,编译器在编译阶段是会比较深入的去检查的,这里由于"a"是常量字符串,不能修改,所以交给arr的指针不能*arr,因此要用const char*,所以报错了。函数模板的特化和类模板的特化用法上都很好理解,实际就是针对一些很特殊的类型做特殊的处理,注意要写template<>,这其实是声明这是个模板,没有模板参数就不写,在函数名或类名后要写实例化的类型。在优先级上,如果已经定义了普通函数,就会最优先调用普通函数,其次找特化,如果实在没有,就会去实例化模板函数。原创 2024-07-11 18:07:37 · 1052 阅读 · 0 评论 -
C++相关概念和易错语法(19)(继承规则、继承下的构造和析构、函数隐藏)
很多人会以为能像函数重载的调用那样根据参数匹配程度来调用,但这个逻辑在继承里走不通,万一我就想利用隐式类型转换调用子的成员函数,但是参数却匹配了父的成员函数呢?父的成员是protected意味着这些成员对外是个秘密,但在自己家庭中不算个秘密,但家庭中的每个人都有义务对外保守这个秘密,即protected无论以何种方式继承对外都不可见。继承的本质是复用,是结构上的继承而不是内容上的继承,近似于在子类中声明了父类的成员变量。由于在父类的析构函数调用后间接使用了父类的成员,所以出现越界访问的情况。原创 2024-07-12 22:01:31 · 795 阅读 · 1 评论 -
C++相关概念和易错语法(20)(赋值兼容转换、多继承、继承与组合)
而一般来讲,protected成员更倾向于类功能的底层,函数更多,父类的书写者不可能去考虑子类调用的情况来适配,这对于书写子类功能的人来说无疑是一场灾难。,我们只能调用该类向外展示的函数接口,其余细节完全不知道,因此如果这个类出现了毛病,有人去维护,它们只需要修bug就行了,只需要保证对外提供的接口是安全的,功能是正常的就可以了。我们会发现,BCD都没有问题,但E就有问题了,E通过两条继承关系链继承了两次A,意味着E里面有两份A的成员变量,这是单继承永远碰不到的情况,这叫。这是子类给父类赋值的一种操作。原创 2024-07-14 00:34:57 · 1331 阅读 · 0 评论 -
C++相关概念和易错语法(21)(虚函数、协变、析构函数的重写)
重写的意义在于子类也有一个新的虚函数表,虽然函数前没有加声明virtual(父类前必须加),当子类显式写了这个函数,就会存到常量区,虚函数表存函数的地址(第一句指令的地址)。至此,我们应该能够理解前面所说虚函数、虚函数表、重写存在的意义了,它们的出现都最终服务于实现一件事——多态,即根据不同类,在调用同一函数时体现出不同状态。也就是说,r无论接收的是A还是B还是C,最终都会被切割成A的模样(A中也有虚函数表),但是内容是不是都一样呢?联系到上面的重写,很快我们就会想到使用virtual修饰父类的析构函数,原创 2024-07-15 15:12:44 · 615 阅读 · 0 评论 -
C++相关概念和易错语法(22)(final、纯虚函数、继承多态难点)
我们要理解访问限定符限制的是什么,是防止其它类调用private的函数,这里p是一个指针,本身就指向B对象的空间,只不过访问方式按A进行。如植物是一种抽象的类,而像苹果、香蕉就是具象的,单独讨论植物太过庞大,没有太大意义,因此我们的重心放在由植物具象出来的苹果,我们可以具体讨论它的成分、营养价值等。要理解这里,我们假设这个指针的类型是子类的,那如果子类又写了一个一模一样的函数构成隐藏,那么就会因为参数和假设的函数完全相同而报错,所以是行不通的。:多态调用函数的核心时动态绑定,也叫运行时绑定。原创 2024-07-15 18:09:49 · 849 阅读 · 0 评论 -
C++相关概念和易错语法(23)(set、仿函数的应用、pair、multiset)
值得注意的是,multiset的大部分接口和set没什么两样,但是在set中有的接口设计会考虑去重,比如insert的返回值,而在multiset中这就没有必要了,所以存在一些不同之处。set的底层引入了红黑树,大致和key模型的二叉搜索树一样,是借助二叉树的特性来存放数据,达到排序和搜索的功能的。常用的是第二种,直接删除某个值,返回值是删除的值的个数。,在上面的代码中4虽然存在,但它会找比4大的值,返回的是5的迭代器,因此erase按左闭右开的规则会删掉4。前面的find是set容器里自带的,原创 2024-07-21 16:33:09 · 896 阅读 · 0 评论 -
C++相关概念和易错语法(24)(map、迭代器分类)
逻辑上也是必须的,因为map底层的平衡树就是依靠key的大小来建立的,改了key之后这个结点的位置就不对了,整棵树都被毁了。我们已经接触过了string、vector、list、deque、stack、queue、priority_queue、map、set、multiset、multimap这些基本容器,出现这样的原因是迭代器也有它的分类,不同的迭代器有不同的功能,有的算法库的函数只能用特定迭代器才能使用。multimap和map的区别类似于multiset和set的区别,都是支持多个key的存储,原创 2024-07-22 15:52:08 · 555 阅读 · 0 评论