C++入门指南:从基础语法到核心特性全解析

1. C++的第一个程序

C++兼容C的绝大部分语法,因此C程序也可以在cpp文件中运行😊

这是一个非常便利的功能,毕竟在某些情况下printfscanf是比cincout好用的

(eg:保留小数点,提高输入输出流效率...

对于.cpp文件,VS会自动调用C++编译器进行编译,而Linux下要用g++编译。

现在,我们就可以试着创建一个.cpp为后缀的文件,试运行一个简单的代码了:

cpp 复制代码
#include<stdio.h>
int main()
{
    printf("Hello, C++!");
    return 0;
}

或:

cpp 复制代码
#include<iostream>
using namespace std;

int main()
{
    cout << "Hello, C++!" << endl;
    return 0;
}

2. 命名空间:namespace

C++经常用于设计是面向对象的程序,如果没有一个作用域,一些函数和类是很容易产生冲突的。

namespace便解决了这个问题,其本质上是定义了一个域,独立于全局域,不同的域中可以出现同名函数。

定义命名空间方法:

cpp 复制代码
namespace myspace //myspace是自定义空间名
{
	//...
}

注意事项:

  • namespace只能定义在全局,可以嵌套定义
  • 项目中多个工程文件的同名文件定义的同名namespace会被认作是同一个,不会冲突
  • C++标准库是std

2.1 命名空间使用

  1. 项目中会通过::来指定命名空间访问
  2. using可以对某个命名空间展开,便于访问其中成员(可以省略::
  3. 在项目中不推荐展开,但日常练习推荐用using namespace std展开标准库

3. C++的输入输出

C++输入输出需要包含库<iostream>,定义了标准的输入输出对象。

  • std::cin是istream类对象,std::cout是outstream类的对象,主要面向窄字符的标准输入输出流
  • C++的cincout可以使输入输出更加方便,不需要手动指定格式,其输入输出可以自动识别变量类型
  • 在包含<iostream>库时编译器可能会间接包含<stdio.h>,如果报错的话手动包含一下就好。

输入输出示范:

cpp 复制代码
int a;
char b; 
double c;
cin >> a >> b >> c;
cout << a  << " " << b << " " << c <<endl;

4. 缺省参数

定义: 定义:缺省参数是指在函数声明时预先设定的默认值。当调用函数时若未提供该参数值,则自动采用默认值;若提供了实参,则使用传入的参数值。

缺省参数分为全缺省和半缺省

  • 全缺省:函数的全部参数都设置了默认值
  • 半缺省:函数的部分参数设置了默认值

关于半缺省的规则:

  1. 函数参数必须从右到左连续缺省,不能间隔跳跃给缺省值
  2. 带缺省参数的函数调用,传参时是从左到右给缺省值
  3. 函数的声明和定义分离的时候,缺省参数不能同时出现,规定只能在函数声明中出现
cpp 复制代码
#include<iostream>
using namespace std;

//声明(全缺省)
void Add(int a = 10, int b = 10);
//半缺省写法
//void Add(int a, int b = 10);

//定义
void Add(int a, int b)
{
    cout << a + b << endl;
}

//错误写法
// void Add(int a = 10, int b = 10)
// {
//     cout << a + b << endl;
// }

int main()
{
    int x = 20;
    int y = 50;
    Add(x, y); //已传入初始值
    Add(); //未传入初始值
    return 0;
}

如果声明和定义中同时出现了缺省值,则会报错:

5. 函数重载

C++中允许在同一作用域中出现同名函数,但有以下要求:

  1. 可以是参数类型不同
  2. 可以是参数个数不同
  3. 可以是参数的类型顺序不同
  4. 如果只有函数的返回值不同,就不是重载函数

以下是合法的重载函数例子:

cpp 复制代码
int Add(int a, int b)
{
    return a + b;
}

//参数类型不同
double Add(double a, double b)
{
    return a + b;
}

//参数数量不同
int Add(int a)
{
    int b = 10;
    return a + b;
}

//参数位置可以交换
int Add(char a, int b)
{
    return a + b;
}

int Add(int b, char a)
{
    return a + b;
}

//错误写法:仅有函数返回值不同
// double Add(int a, int b)
// {
//     return a + b;
// }

返回值不同不能作为重载的条件,因为调用时无法区分。

函数重载允许同一函数名处理多种数据类型,可以有效减轻命名负担。

6. 引用

引用不是新定义一个变量,而是给已有变量起别名,不会另外开辟空间。

使用方法:

cpp 复制代码
int a;
//类型 &  引用别名 = 引用对象
int& ra = a;

6.1 引用的特性

  • 引用在定义时必须初始化
  • 一个变量可以有多个引用(可以对引用再次引用)
  • 引用一旦初始化后就不能改变

引用在底层相当于是一个自动化指针,编译器在汇编时会自动将引用转化为指针,而我们在使用的时候仅需当作普通变量即可。

6.2 引用的使用

作用:引用传参和引用作返回值可以减少拷贝,提高效率,并且可以直接通过引用改变对象

使用案例:

cpp 复制代码
#include<iostream>
using namespace std;

void Swap(int& rx, int& ry)
{
    int tmp = rx;
    rx = ry;
    ry = tmp;
}

int main()
{
    int x = 20;
    int y = 10;
    Swap(x, y);
    cout << x << endl << y << endl;
    return 0;
}

减少拷贝:

引用作返回值时可以直接通过函数修改:

错误示范:

6.3 const 引用

引用const对象时必须使用const引用,但是引用非const对象时既可以选择用或不用。

记住这一点即可:对象的访问权限在引用过程中可以缩小,但是不能放大。

另外,还需注意临时对象具有常性 ,因此需要使用const引用。

临时对象的产生一般出现在以下情形:

  • 通过+ - * / 等运算得到的结果
  • 函数返回值
  • 发生类型转换时

例子:

cpp 复制代码
int a = 10;
const int& ra = a * 3; //运算
const double& rb = a; //类型转换

const引用可以应用于函数传参,这样就不用担心函数会擅自修改原变量了。如果修改就会报错:

cpp 复制代码
void func(const T& val)
{
	//...
}

6.4 指针和引用的关系

引用在使用过程中虽然比指针便携很多,但是两者仍然不可相互替代,各自有各自的特点:

  1. 语法概念上,引用不需要开辟空间 ,但指针需要开辟空间。因为指针需要空间来存储变量的地址。
  2. 定义时,引用必须初始化 ,指针可以选择不初始化
  3. 对象方面,引用一旦定义就不能更改引用对象,但指针可以不断更改对象。
  4. 访问时,可以直接 通过引用来访问变量,但指针需要解引用才能访问。
  5. sizeof的含义不同:对于引用,返回的是原变量 的大小,而指针则是返回地址的大小(例如,在64位环境下就是8个字节)。
  6. 存储内容上,不存在空引用 ,少部分情况会存在野引用 (比如函数返回了局部变量的引用);指针则会出现空指针和野指针的情况。

7. 内联函数:inline

7.1 inline

使用inline修饰的函数是内联函数,使用时,在函数返回值前加上inline即可。

通俗来讲,inline的作用是建议编译器在编译时直接在需要调用函数的地方直接展开函数,不需要再通过地址访问函数,这样可以提高运行效率。

至于为什么是建议,是因为inline仅适用于短小且频繁调用 的函数。如果对代码冗长且经常调用的函数或递归函数使用inline,编译器会默认忽视这条指令。具体的忽视规则因编译器而异。(如果展开就相当于解压缩了,代码可能会从几百行增加至几千甚至几万行,得不偿失)

使用示例:

cpp 复制代码
#include<iostream>
using namespace std;

inline int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	cout << Add(a, b) << endl;
	cout << Add(a, c) << endl;
	cout << Add(a, d) << endl;
	//编译时不会再 call / ret
	return 0;
}

7.2 inline和宏

在C语言中,宏函数的实现很复杂,很容易出错。例如:

cpp 复制代码
//错误示例:
#define Add(a, b) a + b

//正确示例:
#define Add(a, b) ((a)+(b))

一个简单的加法宏也有很多需要留意的地方,一不小心就会中招TvT

  • 宏不需要加分号,如果加上了就相当于引入了一个空语句,在输入输出时会报错。
  • 宏的很多情况都要加上括号,目的是为了控制宏函数的优先级

因此,C++设计inline的目的就是替代宏函数 ,通过inline展开函数可以达到和宏一样的效果,还不容易出错🤓
注意:和一般的函数实现不同,inline不建议声明和定义分离 到两个文件中,否则会导致链接错误。因为inline展开后没有函数地址,链接时找不到函数就会报错。

8. 独属于C++的空指针:nullptr

C语言中的NULL其实是存在很多弊端的。NULL其实是一个宏函数,这是NULLstddef.h中的定义:

c 复制代码
#ifdef NULL
    #ifdef __cplusplus
        #define NULL    0
    #else
        #define NULL    ((void *)0)
    #endif
#endif

https://legacy.cplusplus.com/ 这个网站上,我们也可以看到NULL的具体定义:

C 中,NULL可能会被定义为无类型指针(void*)的常量。

C++ 中,NULL可能会被定义为字面常量0。

但是,NULL的这个特性在某些情况下会成为负担。比如函数重载:

cpp 复制代码
void func(int x)
{
    cout << "int" << endl;
}
void func(char* p)
{
    cout << "pointer" << endl;
}

func(NULL); // 这里会调用 func(int),因为 NULL是 0
func(nullptr); // 调用 func(char*)

这里我们本来想通过NULL调用func(char*),却被编译器当作整数0处理了。

因此C++中引入了一个特殊的关键字:nullptrnullptr可以转换成其他任意类型的指针类型,不会再转换为整数类型。所以,下次如果想表示空指针,使用nullptr会比NULL更加保险~🤗

相关推荐
programhelp_2 小时前
2026 高盛(Goldman Sachs)Coding Interview 真题分享|Design HashMap + 其他面试题完整解析
算法·哈希算法
Pentane.2 小时前
力扣HOT100:T.1 两数之和|循环遍历算法笔记及打卡(12/100)
c++·笔记·算法·leetcode
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【线性扫描贪心】:士兵站队
c++·算法·贪心算法·csp·信奥赛·线性扫描贪心·士兵战队
柠檬07112 小时前
记录bug :C++调用python 路径问题
c++·python·bug
无限进步_2 小时前
二叉树的中序遍历(非递归实现)
开发语言·数据结构·c++·windows·算法·visual studio
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【线性扫描贪心】:糖果传递
c++·刷题·贪心·csp·信奥赛·线性扫描贪心·糖果传递
计算机安禾2 小时前
【数据结构与算法】第48篇:算法思想(三):贪心算法
c语言·开发语言·数据结构·算法·贪心算法·代理模式·图论
BestOrNothing_20152 小时前
C++零基础到工程实战(4.3.1):数组与vector初识——连续内存与动态数组的本质解析
c++·vector·初始化·内存分配·栈区数组·堆区数组
_深海凉_2 小时前
LeetCode热题100-爬楼梯
算法·leetcode·职场和发展