当前位置: 首页 > news >正文

C++基础入门

        在学完了C语言和基础数据结构以后,我们可以开始逐渐开始学C++了。C++作为目前市场上最为热门的几款语言之一,也被人们称为最难的语言。C++有着强大的功能与复杂的语法,需要我们尽全力去好好学习他,掌握他,以提高自己的综合能力。因此,从今天开始,我将慢慢在CSDN上更新我的学习笔记,若有错误之处,烦请您指出来,无尽感激。

        C++是大量包含了C语言语法的,因此,你直接在C++的编译器里面写C语言理论上也是没问题的。但是既然C++是plusplus,当然有他的改进与独到之处。

命名空间

        当我们在写C语言的时候,往往烦心于一件事情:变量命名难题。大部分的语言的函数和变量在命名的时候,因为变量不可重复问题,往往后面要加上一大堆后缀。我们平时在定义函数,定义变量时,往往是在全局域和局部函数域中进行定义的,使用起来有些狭隘,并且若是多个人员同时对进行一个项目,最后整合的时候,往往会出现变量冲突。对于一些变量名,例如rand等,我们在引入了“stdlib.h”头文件后也不用用作变量名字。在C++中,祖师爷发明了一个东西:命名空间域namespace。命名空间域里面可以放变量和函数。而且我们知道变量在不同域中可以重复定义。如下图,当我们没有特别指定的时候,a,b都是使用main函数中的局部变量。当我们想要调用Zeng 命名空间域中的变量时,我们需要用“::”运算符来调用。特别的,若我们想要使用全局变量时,只要在::前面什么都不写就行。若变量在局部函数中,他还具有生命周期,当这个函数运行结束,这个变量也就销毁。但是命名空间域则不会受影响,其生命周期等同于全局域变量的生命周期。

double a = 4.4;namespace Zeng
{char a = 'a';double b = 6.6;
}int main()
{int a = 1;int b = 2;printf("%d %d\n", a, b);//打印1,2printf("%c %lf", Zeng::a, Zeng::b);//打印a,6.60000printf("%lf",::a);//打印4.40000
}

        namespace不仅可以存放函数和变量,也可以进行嵌套。例如在公司项目中,第一小组里面可以嵌套总的salary变量,还可以嵌套每一个小组成员的变量。例如以下代码,如果我们想要调用第一小组的总工资,只要进行一步::就行。若想要调用YA的工资,则需要先调用YU,在调用Salary,他们位于不同的作用域,但是名字一样,不会冲突

namespace First
{int Salary = 10000;namespace YU{int Salary = 1000;}namespace YA{int Salary = 9000;}
}int main()
{printf("%d\n", First::Salary);printf("%d", First::YA::Salary);
}

多个namespace 可以组合调用。例如,我在"stack.h"定义了一个名为YU的namespace,在“Queue.h”中也定义了一个名为YU的namespace,在引用这两个头文件的时候,他们会自己归纳到一起,也就是说,他们在使用的时候,会变成一个命名空间域。这就很方便我们进行使用,我们没有必要反复的定义YU_Queue,YU_Stack,节省了理解成本和时间成本。

我们有三种命名空间的使用方式,第一种是上文提到的每次使用命名空间都在前面加相对的名字。这种是最推荐的用法,虽然与下面两种比较起来较为麻烦,但是是最严谨靠谱的。第二种是部分展开,既把命名空间域中的某一变量给展开为当前使用他的域中的局部变量。第三种是完全展开,既把一个命名空间中的所有变量全部展开为当前局部域中的所有局部变量。后面两种推荐练习时为了节省时间适当使用,但是本人依旧认为第一种是最好的。

namespace Zeng
{int a = 1;int b = 2;
}int main()
{using Zeng::a;using Zeng:}

C++输入与输出 

        首先,我们得先知道C++标准库都放在一个叫做std的命名空间中,这里的标准库包含了很多,而std这个命名空间把他全部包含在一起了,当我们引入相应的头文件时,就可以直接通过std这个命名空间来调用相应的功能。

        <iostream>是C++中我们非常常用的一个库,他是C++的标准输入输出流库。其中包含了几个目前我们需要掌握的对象,函数。同时,我们要知道,在C++的语法中,不需要在库后面加上.h了

1.cin 其中的c表示的是character,面向窄字符输入,用于在控制台中输入数据。in既表示输入。

2.cout 其刚好和cin相反,他是标准的流输出,用于 在控制台中输出数据,显示在控制台上。

3.std::endl是一个函数,表示的是在进行流插入输出插入一个换行字符加刷新缓冲区。

在进行流输入输出的时候,我们还需要使用流运算符,<<是流输出符,>>是流输入符。在C语言中,这两个符号还是位左移和右移符。

C++的输入输出非常方便,他会自动识别格式,不需要手动去输入%d,#lf等等。但是,这并不是说printf和scanf等以前用的函数不需要用了,在更多场合下,我们需要综合这两种输入输出方法。

#include <iostream>int main()
{int a;char b;std::cin >> a >> b;std::cout << 'a' << "\n" << 20 << std::endl << b << std::endl;
}

缺省参数

        缺省参数是C++语法中相对于C语言的一种改进,他允许函数在最开始的时候为参数定义初始值,在调用该函数的时候,当没有给被缺省的参数输入实参时,函数会自动采用初始定义的缺省值。缺省包括全缺省和半缺省。全缺省指的是给所有形参一个初始值,在这种情况下,就算你在调用函数时,一个实参也没有输入,他也能工作。半缺省指的是只给部分形参初始值。注意,当使用半缺省时,半缺省参数必须从右往左依次连续的给初始值,不能间隔跳跃的进行缺省。

        若一个函数带有缺省,C++规定必须从左往右依次给予实参,不能跳跃参数。

        当函数的定义与声明分离时,我们需要再声明处给缺省,而不能在定义中。以上都是老祖宗规定滴,不能违背!

int Add(int a, int b = 20)
{return a + b;
}int Add1(int a = 10, int b = 20)
{return a + b;
}int main()
{std::cout << Add(15) << '\n' << Add1() << std::endl;}

函数重载

        C++相对于C语言,还有一个优点,那就是同一个函数名,可以用在不同的函数上。编译器会自动根据函数的形参不同来选择函数定义。这里的形参不同可以是参数个数不同,也可以是参数类型不同。参数类型不同中还包含了一种不同,那就是参数类型顺序不同。如果只是返回值不同不能构成重载,因为编译器并不能明确到底是需要的是能够返回的函数还是不能返回的函数。

        若一个函数全缺省,同时有一个名字相同的函数没有形参数,那样也会构成函数重载。但是,例如下例,函数f()并不能在编译器中调用,因为编译器无法确定使用的函数究竟是全缺省的函数还是没有形参的函数。

int Add(int a, int b)
{return a + b;
}double Add(float a, double b)
{return a + b;
}int main()
{std::cout << Add(15,20) << '\n' << Add(15.5,20.1) << std::endl;
}int f()
{
}int f(a = 20)
{
}

引用

        引用也是C++中新加入的特性。祖师爷认为指针的使用太过复杂,且不易于理解,于是加入的引用的语法。引用并不是定义一个新的变量,而是给已知的一个变量一个别名,从此,这个别名和原来的名字都能够改变该地址的数据。

        引用定义时必须初始化,没有初始化的引用无法通过编译。一个变量可以有许多别名,但是这个别名一旦跟了这个变量,它就不能再改变,不能在引用其他实体。

int main()
{int a = 10;int& ra = a;int b = 20;ra = b;//此处不是改变引用,是赋值std::cout << a <<endl<< b <<endl<< ra << endl;
}

        引用的使用场景很多,但主要是引用传参和引用做返回值中减少拷贝效率和改变引用对象时同时改变被引用对象。 引用传参基本可以平替指针传参,相对而言引用传参会更方便一点。使用引用传参我们就不需要费脑子去思考指针具体的使用方法以及相关的问题。使用引用传参也能避免空指针等问题。而对于以下的二级指针,引用指针参数的效果也比直接用二级指针来得好。

void ListNode(LNode** Ltp,int x);void ListNode(LNode*& Ltp,int x);

        在引用参数中,有一个叫做const引用的部分,他会起到一个非常奇妙的效果。在讲const引用之前,我们需要先了解一下临时变量。

        临时变量,顾名思义,他是在编译过程中临时产生的变量,他不是我们平时意义上所说的自己定义的temp,它是隐性的,编码者不可见。临时变量诞生于数据交互的过程,例如表达式的计算,类型转换,函数的形参使用和函数返回值。

        const可以引用一个const值,他也可以引用普通对象,普通对象被引用了,只是权限的缩小,但是权限是不可以放大的,也就是说,普通引用不可以引用const对象。

        而对于触及临时变量的引用,必须使用const引用,也就是常引用。对于常量的引用,也需要用常引用,具体为啥,问就是祖师爷的规定。

int main()
{const int a = 10;//int& ra = a;const int ra = a;int b = 20;const int rb = b;//rb--;常引用不可以进行数据的变化const int rc = 30;//rc--;//int& d = a + b;错误const int& d = a + b;double e = 6.6;//int& re = (int)e;错误const int& re = (int)e;return 0;
}

        指针和引用之间有着千丝万缕的关系,他们本质上就是一对兄弟,指针是哥哥,引用是弟弟。虽然我们日常中可能用引用会多一点,但其实他们相辅相成,不可分离。

1.语法概念上引用是引用是变量取别名,不会开空间。但是指针是会开空间的,他本质上是储存变量的地址。

2.引用在定义时必须初始化,指针不一定要初始化。

3.引用在初始化时引用了一个对象后,就锁死了,不能引用其他对象。而指针可以改变指向的目标

4.引用的名就是指向的目标,可以直接改变。而指针还需要解引用才可以访问指向目标。

5.对于sizeof,引用的结果为指向目标类型的大小,而指针是固定的4/8字节。

6.指针很容易出现空指针和野指针的问题,引用很少出现,更安全。下面是引用野指针的一种情况。当f()返回a+b的别名时,因为a和b都是局部变量,出了该函数以后就已经销毁了,因此在使用f的返回值时,会报错。

int& f()
{int a = 10;int b = 20;return a+b;
}int main()
{cout << f() << endl;}

inline

        用inline修饰的函数被称为内联函数,编译时c++会在调用的地方展开内联函数,起到和宏函数一样的效果。但是他却比宏函数要好用一些,因为他可以避开宏函数的一些坑。

首先我们先来细数宏函数中的坑:

        1.为什么不能加分号?答:宏函数的作用是直接替换,如果在定义时直接加了分号,会导致展开后也有分号,如果该宏函数不是用在语句的最后,可能会导致语句提前结束和语法错误。

        2.为什么要加外面的分号?答:同理,因为是直接替换,如果不加括号,可能会导致内部的参数提前和外部的数据进行交互。如以下的Add(),如果外面没加括号,那么就会变成10+20*30.

        3.为什么要加里面的分号?答:加里面的分号是为了防止表达式冲突。因为各个运算符的优先级不同,如果不在里面加上括号,可能会导致本来想要传的没传过去。如下图,y=10&20+30|10,因为加号的优先级更高,因此最后会产生我们不想要的答案。

OK,我们细数完了宏函数的糟糕之处,现在来看看inline。inline的本质还是实现一个函数,但是这个函数会在使用时被展开,无需额外建立栈帧。对于一些短小常用的函数,内联函数可以明显提高效率。但是,inline对于编译器只是一种建议,对于代码长而多的代码,编译器不会调用inline,而是会忽视它。

inline不建议声明和定义分离到两个文件,会导致连接错误。当函数被inline展开,那就不会有函数的地址,链接会发生错误。当然,同文件还是可以进行声明定义的。

#define Add(a,b) ((a)+(b))inline int ADd(int a, int b)
{return a + b;
}int main()
{int x = Add(10,20)*30;int y = Add(10&20,30|10);int c = Add(10, 20);int cc = ADd(10, 20);}

nullptr 

        我们在c语言中,常用NULL来表示空指针,由于C++如下的定义问题,NULL被定义成了常量0,当我们想要int*类型的NULL时,却得到的是int类型的。假设我们用int*进行类型强转,也没法完全解决该问题。为了解决这个问题,C++引入了nullptr,nullptr可以被转换成任意类型的指针类型,并且他只能被隐式的转换为指针类型,而不会转换成int类型。

#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif
#endif

        

        


http://www.mrgr.cn/news/83606.html

相关文章:

  • centos9设置静态ip
  • 浅谈云计算01 | 云计算服务的特点
  • zerox - 使用视觉模型将 PDF 转换为 Markdown
  • Python Selenium库入门使用,图文详细。附网页爬虫、web自动化操作等实战操作。
  • Mysql常见知识点
  • 算法题(33):长度最小的子数组
  • ASP.NET Core - 自定义中间件
  • 计算机的错误计算(二百一十)
  • Linux权限揭秘“Root与Sudo”
  • 代码随想录day34 | 62.不同路径 63.不同路径II
  • Perl语言的网络编程
  • 单倍型、候选基因关联分析
  • 高等数学学习笔记 ☞ 一元函数微分的基础知识
  • 继续以“实用”指导Pythonic编码(re通配表达式)(2024年终总结②)
  • 【江协STM32】10-2/3 MPU6050简介、软件I2C读写MPU6050
  • brpc之baidu_protocol
  • 三台Centos7.9中Docker部署Redis集群模式
  • mac homebrew配置使用
  • 卷积神经网络 (CNN, Convolutional Neural Network) 算法详解与PyTorch实现
  • Git的学习和常见问题
  • Spring 项目 基于 Tomcat容器进行部署
  • Oopsie【hack the box】
  • Go跨平台UI开发之wails的使用(1)项目初始化
  • 26个开源Agent开发框架调研总结(2)
  • 一步迅速学会 B+ 树 的核心概念
  • SpringAOP前置——代理模式