C++入坑基础知识点

当学习了C语言之后,很多的小伙伴都想进一步学习C++,但两者有相当一部分的内容都是重叠的,不知道该从哪些方面开始入门C++,这篇文章罗列了从C到C++必学的入门知识,学完就算是踏入C++的大门了。

1. 命名空间

写C的时候,偶尔可能不小心就和库函数冲突了,又或者当两个程序员以相同的名字命名变量或函数,就会发生冲突。C++就提出了一个很好的解决方案------命名空间

命名空间的关键字是namespace,定义方式如下:

【定义方式】

cpp 复制代码
namespace N1
{
    int a = 3;
    int b = 4;
​
    int Add(int x1, int x2)
    {
        return x1 + x2;
    }
​
    struct Node
    {
        struct Node* next;
        int val;
    };
​
    namespace N2
    {
        int c = 0;
        int d = 0;
    }
}

从示例中可以看出,命名空间中可以包含变量、函数、类型以及嵌套另一个命名空间

【使用方式】

1. 命名空间+作用域限定符

cpp 复制代码
int main() {
    printf("%d\n", N1::a);
    return 0; 
}

2. using将命名空间中某个成员引入

cpp 复制代码
using N1::b;
int main() {
    printf("%d\n", N1::a);
    printf("%d\n", b);
    return 0; 
}

3. using namespace 变量空间

cpp 复制代码
using namespace N1;
int main() {
    printf("%d\n", a);
    printf("%d\n", b);
    return 0; 
}

2. 输入输出

其实C语言的printf/scanf在C++中还可以使用,不过C++有独有的输入输出方式,就是cin/cout

【使用说明】

  1. 必须包含iostream头文件,注意是<iostream>

  2. 需要使用命名空间std,因为cin/cout在其中的(日常练习可以这么做,但做项目的时候最好使用 std::cin 的方式展开)

  3. 使用时要用<<流插入操作符/>>流提取操作符,不知道是什么没关系,看示例是怎么用的就行,cout用<<,cin用>>

  4. 变量之间也是用<<或>>连接,如果需要换行用endl

【示例】

cpp 复制代码
#include <iostream>
using namespace std;
​
int main()
{
    cout << "hello world!" << endl;
    return 0;
}

【解惑】

我们有了printf和scanf为什么还要学习cin和cout呢,因为其可以自动识别变量的类型,相对来说就方便了很多,不用考虑太多。不过cin/cout相对于前者慢一些,在大量数据需要输入输出时就会有较大的时间差距。

3. 缺省参数

声明或者定义 函数时为函数参数指定一个缺省值,当函数没有传递对应的参数时,就用缺省参数来代替(有点像备胎的感觉doge)

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

void Func (int a = 10)
{
    cout << a << endl;
}

int main()
{
    Func(); //输出10
    Func(100); //输出100
    return 0;
}

【分类】

全缺省参数:所有形参都加上了缺省参数

cpp 复制代码
void Func(int a = 10, int b = 20, int c = 30)
{
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
}

int main()
{
    Func(9, 8, 5);
    Func(9, 8);
    return 0;
}

半缺省参数:只有部分参数给缺省值

cpp 复制代码
void Func(int a, int b = 20, int c = 30)
{
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
}

//错误写法
//void Func(int a = 10, int b, int c = 30)
//void Func(int a = 10, int b = 20, int c)
//void Func(int a = 10, int b, int c)

int main()
{
    Func(9);
    Func(9, 8);
    Func(9, 8, 5);
    return 0;
}

注意:

  1. 半缺省参数只能从右往左给,不能跳跃给,也是为了避免传错参数,出现歧义

  2. 缺省参数不能同时在定义和声明中出现,一般在声明中给

  3. 缺省值必须是全局变量或常量

4. 函数重载

C++中允许在同一个作用域中出现参数列表不同 (参数个数或类型不同)的同名函数,称这种现象为函数重载

【示例】

cpp 复制代码
#include <iostream>
using namespace std;
​
//参数个数不同
void f1(int x, int y)
{
    cout << "void f1(int x, int y)" << endl;
}
​
void f1(int m)
{
    cout << "void f1(int m)" << endl;
}
​
//参数类型不同
void f2(int m)
{
    cout << "void f2(int m)" << endl;
}
​
void f2(double m)
{
    cout << "void f2(double m)" << endl;
}
​
//参数类型顺序不同,本质就是类型不同
void f3(int x, double y)
{
    cout << "void f3(int x, double y)" << endl;
}
​
void f3(double x, int y)
{
    cout << "void f3(double x, int y)" << endl;
}
​
int main()
{
    f1(10, 20);
    f1(10);
    cout << endl;
    f2(10);
    f2(1.0);
    cout << endl;
    f3(10, 1.0);
    f3(1.0, 10);
​
​
    return 0;
}

【原理】

C++的编译器会对函数进行修饰 ,一种函数变量类型对于一种编码,所以不同的变量类型或者个数对应不同的汇编代码,所以函数名相同并不影响其分别不同的函数。

而C语言中,相同的函数名是区分不开的。

(此部分需要对程序的翻译和执行部分有一定的了解,可以暂时跳过)

5. 引用

引用是对已经创建的变量起别名,操作符是&

【示例】

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

int main()
{
    int a = 1;
    int& b = a;

    cout << a << endl;
    cout << b << endl;
    //打印结果都是1

    cout << &a << endl;
    cout << &b << endl;
    //打印出来是同一个地址,也就意味着在同一个空间
    return 0;
}

结果如下:

所以当其中一个加的时候,另一个也加,效果如下:

注意:

  1. 引用和实体的类型要相同,鲁迅和周树人都是人

  2. 引用在定义时必须初始化

  3. 一个变量可以多个引用,也就是可以多个别名

  4. 引用一个实体就不能再引用其他的实体,鲁迅是周树人的别名,就不能再用"鲁迅"代指张三了

  5. 引用不能作函数局部变量的返回值,因为返回值的空间在退出函数之后就销毁了,而引用与它共用一块空间,所以此时为非法访问空间。当然,如果是全局变量/函数外局部变量/静态变量/堆上变量也是可以的作为返回值的。

​​​​​​​

【使用方式】

  1. 输出型参数
cpp 复制代码
#include <iostream>
using namespace std;
​
void Swap1(int& x, int& y)
{
    int temp = x;
    x = y;
    y = temp;
} //节省拷贝的空间,在传大的对象时对比更加明显
​
void Swap2(int* m, int* n)
{
    int temp = *m;
    *m = *n;
    *n = temp;
} //需要额外开辟一块空间存临时变量
​
int main()
{
    int a = 1, b = 2;
    Swap1(a, b);
    cout << a << " " << b << endl;

    int c = 1, d = 2;
    Swap2(&c, &d);
    cout << c << " " << d << endl;
​
    return 0;
}
  1. 函数返回值(注意返回局部变量时不可用)
cpp 复制代码
#include <time.h>
#include <iostream>
using namespace std;

struct A
{ 
    int a[10000]; 
};

A a; 

A TestFunc1() 
{ 
    return a;
}

A& TestFunc2()
{ 
    return a;
}

int main()
{
    size_t begin1 = clock();
    for (size_t i = 0; i < 100000; ++i)
        TestFunc1();
    size_t end1 = clock();

    size_t begin2 = clock();
    for (size_t i = 0; i < 100000; ++i)
        TestFunc2();
    size_t end2 = clock();

    cout << "TestFunc1 time:" << end1 - begin1 << endl;
    cout << "TestFunc2 time:" << end2 - begin2 << endl;
    return 0;
}

这效率,赢麻了!

有小伙伴又要问了,引用这么香,谁还用指针呀?

那我们就得来看看指针和引用的区别了:【面试考点】

语法:

  1. 引用是别名,不开空间,指针是地址,需要开空间存地址
  2. 引用必须初始化,指针可以初始化也可以不初始化
  3. 引用不能改变指向,指针可以(不可替代的关键)
  4. 引用相对更安全,没有空引用,但是有空指针,容易出现野指针,但是不容易出现野引用
  5. sizeof(引用为引用类型的大小,指针为地址的大小)、++(引用实体+1,指针偏移一个类型的大小)、解引用访问(引用不用解引用)等方面的区别

底层:

汇编层面上,没有引用,都是指针,引用编译后也转换成指针了

总结:C++的引用,对指针使用比较复杂的场景进行一些替换,让代码更简单易懂,但是不能完全替代指针,因为引用定义后,不能改变指向

【常引用】

如果我需要一个不可被修改的别名,就会使用到常引用

cpp 复制代码
//代码一:正常引用(权限平移)
int a = 10;
int& ra = a;
​
//代码二:会报错,本来我设的变量不可被改,但是你的引用可以改,相当于给它的权限过大了
const int b = 10;
int& rb = b;
​
//代码三:常引用,相当于创建了一个不可修改的别名(权限缩小)
int c = 10;
const int& rc = c;

//代码四:d转换成int需要生成临时变量,再将临时变量赋给rd引用,
//而临时变量是常量,所以rd要用常量引用,此代码没有const是编译错误的
double d = 3.14;
const int& rd = d;

6. 内联函数

一般来说,函数调用都会建立栈帧,而如果有方式可以避免栈帧的调用,可以大大提高运行的效率。

C++引入了一个关键字inline,以inline修饰的函数叫做内联函数 ,编译时C++编译器会在调用内联函数的地方展开 (函数调用替换为函数体),没有函数调用建立栈帧的开销,从而大大提升运行效率。

【示例】

cpp 复制代码
inline int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int ret = Add(3, 4);
	cout << ret << endl;
	return 0;
}

其实没什么好说的,就是在函数前加上inline。

不过,编译器会判断此代码是否大小合适,如果比较大的话还是会建立栈帧,避免在调用处展开导致程序膨胀。

注意:

  1. 不能是递归的函数,且最好规模较小频繁调用的函数。

  2. 不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

【拓展】

【面试考点】如果在头文件中包含了一个函数的声明和定义,导致出现链接错误,该如何处理?

  1. 声明和定义分离

  2. 用static修饰函数,链接属性只在当前文件可见,不会进符号表(大函数)

  3. inline修饰,没有进符号表,不会出现链接冲突

有小伙伴就要问了,好像C语言里的宏也可以做到类似的效果(不建立栈帧就能调用)呀?

但是宏也是有缺点的,详细的内容可看这篇文章-链接

在C++中,有很多设计都可以替代宏:const,enum,inline

7. auto关键字

在C++中,有的类型的名字会很长,所以很容易会出错,而这就引出了auto关键字

这个关键字的作用就是自动识别类型

当然,除了用在比较长的类型名上,还可以用于for循环,这个也是C++特有的

示例如下:

cpp 复制代码
int main()
{
    int array[] = { 1,2,3,4,5 };
    for (auto& e : array)
    {
        e *= 2;
    }
​
    for (auto e : array)
    {
        cout << e << " ";
    }
​
    return 0;
}

上面这段代码等价于:

cpp 复制代码
int main()
{
    int array[] = { 1,2,3,4,5 };
    for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
    {
        array[i] *= 2;
    }
​
    for (int* p = array; p < array + sizeof(array) / sizeof(array[0]); p++)
    {
        cout << *p << " ";
    }
​
    return 0;
}

可见这里可以省去了不少的代码,不过要注意如果不是遍历数组就不能这么操作了

【注意点】

  1. ​​​​​​​auto声明指针类型时,用auto和auto*没有区别,但用auto声明引用类型时必须加&

  2. auto不能作为函数参数和返回值,也不能用来声明数组

8. 空指针nullptr

NULL可作常量0,也可作无类型指针的常量,

一般情况下,NULL会被编译器认为是0常量,但是我们想用它的时候往往是当作指针来用,为了避免会出现麻烦的情况,所以C++又设计了一个nullptr指针,就是表示指针空值 ,即**(void*)0**

这篇内容很多,可以收藏下来慢慢看~感谢大家的点赞和支持👍

相关推荐
hacker7072 小时前
探索数据的艺术:R语言与Origin的完美结合
开发语言·r语言·origin
炸鸡配泡面2 小时前
Qt 12.28 day3
java·开发语言
get_money_2 小时前
代码随想录38 322. 零钱兑换,279.完全平方数,本周小结动态规划,139.单词拆分,动态规划:关于多重背包,你该了解这些!背包问题总结篇。
java·开发语言·笔记·算法·动态规划
mahuifa4 小时前
C++(Qt)软件调试---VS性能探查器(27)
c++·qt·内存泄漏·vs性能探查器·cpu性能分析
不听话的好孩子4 小时前
基于深度学习(HyperLPR3框架)的中文车牌识别系统-Qt开发UI
开发语言·qt·ui
天马行空_4 小时前
周记-唐纳德的《计算机程序设计艺术》
c++
SomeB1oody5 小时前
【Rust自学】7.6. 将模块拆分为不同文件
开发语言·后端·rust
向宇it5 小时前
【从零开始入门unity游戏开发之——C#篇36】C#的out协变和in逆变如何解决泛型委托的类型转换问题
java·开发语言·unity·c#·游戏引擎
冀晓武6 小时前
C++ 设计模式:观察者模式(Observer Pattern)
c++·观察者模式·设计模式
捕鲸叉6 小时前
C++设计模式之行为型模式概述,它们的目的与特点
c++·设计模式