文章目录
前言
大家好,这是我第一次写博客, 希望通过写博客的方式巩固我学习的内容。当然,如果能给大家带来一些收获那更好。
这一篇内容是C++的入门学习,本节内容不会很难,大部分是关于引入,后续关于C++的学习难度将会持续上升,所以我们要打好基础,为后面做准备。
首先我们要了解C++的历史,可以说C++是C语言的升级版,本贾尼博士在使用C语言的过程中,由于在一些功能使用上的不方便,从而进行修改,完善,从而创造了C++,C++兼容C语言的绝大多数语法,下面我们要写第一个C++程序了。
1.C++第一个程序
C++中需要把定义文件代码后缀改为.cpp,vs编译器看到是.cpp就会调用C++编译器编译,如果还是使用.C后缀可能会出现报错,不能识别C++的语法。
#include<stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
这个是两个语言打印 “hello world” 的对比,下面是C++
#include<iostream>
using namespace std;
int main()
{
cout << "hello world" << endl;
return 0;
}
这里iostream,std,<<,end看不懂没关系,后面都会逐步讲到
2.命名空间
2.1namespace的价值
在C/C++中,变量、函数和后⾯要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
#include <stdlib.h>
namespace space1
{
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
}
namespace本质是定义出一个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不会与<stdlib.h>中的rand函数冲突。
namespace只能定义在全局,当然他还可以嵌套定义。
项⽬⼯程中多⽂件中定义的同名namespace会认为是⼀个namespace
2.2命名空间的使用
编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间⾥⾯去查找。
using N::b;
int main()
{
printf("%d\n", N::a);方法一
printf("%d\n", b); 引用后可以直接用。
return 0;
}
3.C++的输入与输出
1.<iostream 是 Input Output Stream 的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输出对象。
2.std::cin 相当于C语言中的scanf输入,std::cout相当于C语言中的printf输出。
在使用了using namespace std;后,可以省略std,直接写cin,cout。cin,cout可以自动识别输入,输出的类型,比C语言有优势。
3.std::endl相当于C语言中的“\n”,是换行符,起的作用是一样的,当然也可以用’\n’。
4.<<是流插⼊运算符,>>是流提取运算符。
4.缺省参数
缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调⽤该函数时,如果没有指定实参
则采⽤该形参的缺省值,否则使⽤指定的实参,缺省参数分为全缺省和半缺省参数。
void Func(int a = 0)
{
cout << a << endl;
}
这里给a了一个初始值,如果使用Func函数时未传参,则a等于初始值0,如果传参了,就是传入的值。
void Func2(int a,int b=10,int c= 20)
缺省参数,不能跳着传值,并且从右边来必须连续的给缺省值。
函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值。
5.函数重载
C++⽀持在同⼀作⽤域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调⽤就表现出了多态⾏为,使⽤更灵活。C语⾔是不⽀持同⼀作⽤域中出现同名函数的。
1.参数类型不同
int Add(int left, int right)
double Add(double left, double right)
2.参数个数不同
void f()
void f(int a)
3.参数类型顺序不同
void f(int a, char b)
void f(char b, int a)
6.引用
6.1引用的概念和定义
引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间,它和它引⽤的变量共⽤同⼀块内存空间。
int main()
{
int a = 0;
//引⽤:b和c是a的别名
int& b = a;
int& c = a;
//也可以给别名b取别名,d相当于还是a的别名
int& d = b;
++d;
//这⾥取地址我们看到是⼀样的
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
return 0;
}
这里他们的地址都是一样的,因为就是一个变量的不同名字罢了。
6.2引用的特性
1.引⽤在定义时必须初始化
int a = 10;
// 编译报错:“ra”: 必须初始化引⽤
//int& ra;
int& b = a;
2.⼀个变量可以有多个引⽤
int& b = a;
int& c = a;
3.引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体
int a=0;
int& b = a;
int c = 20;
// 这⾥并⾮让b引⽤c,因为C++引⽤不能改变指向,
// 这⾥是⼀个赋值
b = c;
引用和指针的功能类似,两者实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。
6.3 const引用
可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤。
不需要注意的是类似int& rb = a3; double d = 12.34; int& rd = d;
这样⼀些场景下a3的和结果保存在⼀个临时对象中,
int& rd = d 也是类似,在类型转换中会产⽣临时对象存储中间值,也就是时,rb和rd引⽤的都是临时对象,⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以。
int main()
{
const int a = 10;
// 编译报错:error C2440: “初始化”: ⽆法从“const int”转换为“int &”
// 这⾥的引⽤是对a访问权限的放⼤
//int& ra = a;
// 这样才可以
const int& ra = a;
// 编译报错:error C3892: “ra”: 不能给常量赋值
//ra++;
// 这⾥的引⽤是对b访问权限的缩⼩
//rb++;
int b = 20;
const int& rb = b;
// 编译报错:error C3892: “rb”: 不能给常量赋值
return 0;
}
int a = 10;
const int& ra = 30;
const int& rb = a*3;
double d = 12.34;
const int& rd = d
这里可以自己去了解
7.inline内联函数
⽤inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联函数就需要建⽴栈帧了,就可以提⾼效率。
C语⾔实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不⽅便调试,C++设计了inline⽬的就是替代C的宏函数。
8.nullptr
NULL实际是⼀个宏,在传统的C头⽂件(stddef.h)中,可以看到如下代码:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL
#endif
#endif
在 C++ 中,使用传统的 NULL 表示空指针可能会遇到麻烦。因为 NULL 可能被定义为整数 0,这在函数重载时(例如同时存在 f(int) 和 f(int*))会导致混淆:调用 f(NULL) 可能意外地匹配到 f(int) 而非期望的指针版本 f(int*)。为了解决这种类型安全问题,C++11 引入了 nullptr。nullptr 是一个特殊的关键字,它拥有自己的类型 (std::nullptr_t),只能被隐式转换为各种指针类型,而不能被当作整数。因此,使用 nullptr 能明确地表示空指针,避免歧义,让代码更健壮和清晰。在现代 C++ 开发中,推荐始终使用 nullptr 来代替 NULL。