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

C++新手入门指南:从基础概念到实践之路

C++ 继承了 C 语言的高效性和灵活性,同时新增了面向对象编程的特点。这使得 C++ 既可以进行底层系统编程,又能进行面向对象的软件设计。在面向对象编程方面,C++ 支持封装、继承和多态三大特性。 


💯C++ 初印象 

语言的发展就像是练功打怪升级一样,也是逐步递进,由浅入深的过程。我们先来看下C++的历史版本。 

😏C++的发展史: 

C++还在不断的向后发展。但是:⭐现在公司主流使用还是C++98C++11,所有大家不用追求最新,重点将C++98和C++11掌握好,等工作后,随着对C++理解不断加深,有时间可以去琢磨下更新的特性。

 


💯关键概念深入探索

(一)命名空间

命名空间是 C++ 中用于避免命名冲突和组织代码的重要机制。它可以将变量、函数、类等标识符包含在一个逻辑空间中,避免与其他代码中的标识符发生冲突

🌷如图:

😏我们加入命名空间就能解决这个问题: 

定义和使用方法

可以使用namespace关键字来定义命名空间。例如:

namespace MyNamespace {

    int myVariable = 10;

    void MyFunction() 
        {

            std::cout << "Hello from MyNamespace!" << std::endl;

        }

}

要使用命名空间中的成员,可以使用作用域解析运算符 :: 来指定命名空间。例如:MyNamespace::MyFunction();。也可以使用using namespace关键字将命名空间中的标识符导入到当前代码中,以便更方便地使用。例如:using namespace MyNamespace;,之后就可以直接使用命名空间中的标识符,如MyFunction();。

引入命名空间成员的方式

  • 使用作用域解析运算符::对命名空间成员进行限定,如MyNamespace::myVariable。
  • 使用using命名空间成员名,如using MyNamespace::myFunction;,之后可以直接使用myFunction而无需再输入命名空间前缀。
  • 使用using namespace命名空间名,如using namespace MyNamespace;,声明了在本作用域中要用到命名空间MyNamespace中的成员,在使用该命名空间的任何成员时都不必再使用命名空间限定。


(二)输入输出

#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
cout<<"Hello world!!!"<<endl;
return 0;
}

C++ 与 C 语言的输入输出方式有很大不同。在 C 语言中,通常使用scanf和printf进行输入输出操作,需要给出格式控制字符串,并且记忆繁多的占位符。而 C++ 使用iostream头文件中的cin和cout进行输入输出,更加方便易用。

cin是标准输入流,通常与流提取运算符>>结合使用。C++ 编译器会根据要输入值的数据类型,选择合适的流提取运算符来提取值,并把它存储在给定的变量中。例如:int a, b; cin >> a >> b;。

cout是标准输出流,通常与流插入运算符<<结合使用。C++ 编译器会根据要输出变量的数据类型,选择合适的流插入运算符来显示值。例如:cout << "Hello, World!" << endl;。

endl表示换行,与 C 语言中的\n作用相同,它是 “end of line” 的缩写。


(三)缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个缺省值🌟在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参

分类

  • 全缺省参数:给所有参数都指定一个默认值。例如:void TestFunc(int a = 10, int b = 20, int c = 30)。
  • 半缺省参数:给部分参数指定一个默认值。例如:void TestFunc(int a, int b = 20, int c = 30)。

 

❗注意事项:

  1. 半缺省参数必须从右往左依次来给出,不能间隔着给。
  2. 缺省参数不能在函数声明和定义中同时出现。
  3. 缺省值必须是常量或者全局变量。
  4. C 语言不支持缺省参数。


(四)函数重载

函数重载是 C++ 的一种特殊情况,允许在同一作用域中声明几个功能类似的同名函数,只要它们的参数列表不同就可以。参数列表不同包括参数的个数不同、类型不同或顺序不同。

例如:

int Add(int left, int right) {

cout << "int Add(int left, int right)" << endl;

return left + right;

}

double Add(double left, double right) {

cout << " double Add(double left,double right)" << endl;

return left + right;

}

这里有两个同名函数Add,但参数类型不同,一个是int类型,一个是double类型。

👀C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同👇 

#include<iostream>
using namespace std;

// 1、参数类型不同
int Add(int left, int right)
{
    cout << "int Add(int left, int right)" << endl;
    return left + right;
}
double Add(double left, double right)
{
    cout << "double Add(double left, double right)" << endl;
    return left + right;
}

// 2、参数个数不同
void f()
{
    cout << "f()" << endl;
}
void f(int a)
{
    cout << "f(int a)" << endl;
}

// 3、参数类型顺序不同
void f(int a, char b)
{
    cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
    cout << "f(char b, int a)" << endl;
}
int main()
{
    Add(10, 20);
    Add(10.1, 20.2);
    f();
    f(10);
    f(10, 'a');
    f('a', 10);
    return 0;
}

返回值类型不同不能作为重载函数原因是,在调用函数时,编译器是通过参数列表来确定调用哪个函数,而不是返回值类型。如果仅靠返回值类型不同来重载函数,编译器无法确定应该调用哪个函数。


(五)引用

1.引用的概念

引用就如同给已存在的变量取了一个别名。👻比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"。,这里的李逵就相当于一个已存在的变量,“铁牛” 和 “黑旋风” 就是对李逵的引用。在编程中,引用变量和它所引用的原始变量共用同一块内存空间,这意味着对引用变量的操作实际上就是对原始变量的操作。

2.引用的特点

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

    • 这就好比你不能只说有一个别名,但不指出这个别名对应哪个具体的人。在编程中,如果不初始化引用,就不知道这个引用指向哪个具体的变量,会导致编译错误。
    • 例如:int a = 10; int& b; // 错误,引用 b 未初始化。正确的做法是:int a = 10; int& b = a; // 正确,引用 b 初始化为变量 a
  2. 一个变量可以有多个引用

    • 继续以李逵的例子来说,李逵除了 “铁牛” 和 “黑旋风” 这两个别名外,可能还有其他的称呼。在编程中,一个变量可以被多个引用指向,每个引用都代表这个变量的不同别名。
    • 例如:int a = 10; int& b = a; int& c = a; // 变量 a 有两个引用 b 和 c
  3. 引用一旦引用一个实体,再不能引用其他实体

    • 就像李逵被称为 “铁牛” 后,这个 “铁牛” 的别名就不能再用来称呼其他人了。在编程中,一旦引用被初始化为某个变量,它就不能再指向其他变量。
    • 例如:int a = 10; int b = 20; int& c = a; c = b; // 这里是将变量 b 的值赋给引用 c 所指向的变量 a,而不是让引用 c 指向变量 b

3.常引用的应用
void TestConstRef()
{
  const int a = 10;
  /*int& ra = a;  该语句编译时会出错,普通引用(int&)不能绑定到常量上,
因为普通引用是可修改的,它可以通过引用改变所绑定的变量的值。
而常量不能被修改,所以会出现编译错误。*/
  const int& ra = a;
  /* int& b = 10; 该语句编译时会出错,10是一个常量值,不是一个变量。
普通引用必须绑定到一个变量上,不能绑定到常量值,所以会编译错误。*/
  const int& b = 10;
  double d = 12.34;
  /*int& rd = d;该语句编译时会出错,d是一个double类型的变量,
而普通引用(int&)要求所绑定的对象类型必须完全一致。这里类型不匹配,所以会出现编译错误。*/
  const int& rd = d;
}

 常引用可以引用普通变量、常量或字面常量。例如:const int& ra = a;。

4.引用做参数和返回值的场景
  • 做参数:可以避免值传递时的拷贝开销,提高效率。例如:void swap(int& a, int& b)。
    void Swap(int& left, int& right)
    {
     int temp = left;
     left = right;
     right = temp;
    }
  • 做返回值:可以返回一个变量的引用,实现链式操作。 
    int& Count()
    {
     static int n = 0;
     n++;
     // ...
     return n;
    }

5.引用与指针的区别
  1. 不存在空引用,引用必须连接到一块合法的内存;而指针可以为空。
  2. 一旦引用被初始化为一个对象,就不能被指向到另一个对象;指针可以在任何时候指向到另一个对象。
  3. 引用必须在创建时被初始化;指针可以在任何时间被初始化。
  4. 引用在 sizeof 中结果为引用类型的大小;指针在 sizeof 中是地址空间所占字节个数(32 位平台下占 4 个字节)。
  5. 有多级指针,但没有多级引用。
  6. 引用比指针更安全,访问实体的方式不同,指针需要显式解引用,引用编译器自己处理。


(六)内联函数

内联函数是一种在编译时将函数体插入到调用处的函数,以减少函数调用的开销。

🔥​​​​​​​以下是用 C++ 代码解释内联函数:

#include <iostream>

// 普通函数
int addNormal(int a, int b) {
    return a + b;
}

// 内联函数
inline int addInline(int a, int b) {
    return a + b;
}

int main() {
    int x = 5, y = 10;

    // 调用普通函数
    int resultNormal = addNormal(x, y);
    std::cout << "Result using normal function: " << resultNormal << std::endl;

    // 调用内联函数
    int resultInline = addInline(x, y);
    std::cout << "Result using inline function: " << resultInline << std::endl;

    return 0;
}

在上述代码中,定义了两个函数addNormaladdInline,分别作为普通函数和内联函数实现两个整数相加的功能。在main函数中分别调用这两个函数,可以看到内联函数在编译时会将函数体直接插入到调用处,避免了函数调用的开销

需要注意的是,编译器并不一定会将addInline函数真正内联处理,它会根据具体情况进行优化决策。如果函数体比较复杂或者有其他不适合内联的情况,编译器可能会选择不进行内联。


(七)auto 关键字(C++11)

在 C++11 中,auto关键字用于自动类型推导它可以根据初始化表达式的类型自动推断出变量的类型。

👉例如:auto i = 10;,这里i的类型会被自动推导为int

auto不能推导的情况包括:

  1. 函数参数的类型不能用auto推导。
  2. 数组的类型不能用auto推导,但是可以用auto推导数组的元素类型,然后结合std::begin和std::end来遍历数组。

🔥代码解释: 

#include <iostream>
#include <vector>
#include <algorithm>

// 正常使用 auto 进行类型推导
void normalAutoUsage() {
    auto i = 10;
    std::cout << "Type of i is deduced as int. Value of i: " << i << std::endl;

    auto str = "Hello, world!";
    std::cout << "Type of str is deduced as const char*. Value of str: " << str << std::endl;
}

// 函数参数不能用 auto 推导
void cannotDeduceFunctionParams(auto a) {
    // 错误,不能用 auto 推导函数参数类型
}

// 数组的类型不能用 auto 推导,但可以推导元素类型并遍历
void arrayDeduction() {
    int arr[] = {1, 2, 3, 4, 5};
    // auto arr2 = arr; // 错误,不能用 auto 推导数组类型
    for (auto element : arr) {
        std::cout << "Array element: " << element << std::endl;
    }
}

int main() {
    normalAutoUsage();
    // cannotDeduceFunctionParams(10); // 会编译错误
    arrayDeduction();
    return 0;
}


(八)基于范围的 for 循环(C++11)

在 C++11 中,基于范围的 for 循环提供了一种更简洁的方式来遍历容器或数组。

传统的 for 循环遍历数组(C++98)

int arr[] = {1, 2, 3, 4, 5};
int len = sizeof(arr) / sizeof(arr[0]);
std::cout << "传统 for 循环遍历数组:";
for (int i = 0; i < len; i++) {
    std::cout << arr[i] << " ";
}
std::cout << std::endl;

 基于范围的 for 循环遍历数组(C++11)

int arr[] = {1, 2, 3, 4, 5};
std::cout << "基于范围的 for 循环遍历数组:";
for (int& x : arr) {
    std::cout << x << " ";
}
std::cout << std::endl;

 for (int& x : arr):这是 C++11 中基于范围的 for 循环语法。在这里,arr 是要遍历的数组。int& x 表示定义了一个引用类型的变量 x,它会依次引用数组中的每个元素使用引用的好处是可以在循环体内修改数组元素的值,如果只是读取元素的值,也可以使用 const int& x 或者 int x


(九)指针空值 nullptr

在 C++11 中,引入了nullptr作为指针的空值。与 C++98 中的指针空值(如NULL或 0)相比,nullptr具有更好的类型安全性。

nullptr可以明确表示指针为空,避免了与整数类型的混淆。

👇以下是用 C++ 代码解释 C++11 中的指针空值 nullptr

#include <iostream>

void func(int num) {
    std::cout << "Called function with integer parameter: " << num << std::endl;
}

void func(int* ptr) {
    if (ptr == nullptr) {
        std::cout << "Called function with pointer parameter (nullptr)." << std::endl;
    } else {
        std::cout << "Called function with pointer parameter (non-null): " << *ptr << std::endl;
    }
}

int main() {
    // 使用 nullptr 调用指针参数的函数
    func(nullptr);

    int num = 10;
    // 使用整数地址调用指针参数的函数
    func(&num);

    // 使用 0 调用可能产生歧义
    func(0);

    return 0;
}

在这个例子中:

  • 定义了两个重载的函数 func,一个接受整数参数,另一个接受指针参数。
  • 在 main 函数中,分别使用 nullptr、整数变量的地址和 0 来调用 func 函数。
  • 使用 nullptr 可以明确地表示调用指针参数的函数,并且不会产生歧义。而使用 0 可能会导致调用歧义,因为编译器可能不确定是调用整数参数的函数还是指针参数的函数(在某些情况下,0 可能被视为整数常量,也可能被视为空指针常量)。

通过这个例子可以看出,nullptr 在表示指针空值时具有更好的类型安全性。


💯C++资源推荐

 

1.在线课程

  • C++ 程序设计北京大学:由刘家瑛教授和郭炜教授授课,面向已经掌握 C 语言的学员,将掌握 C++ 语言中的类、对象、运算符重载、继承、多态等面向对象的机制,以及模版、STL 等泛型程序设计的机制。本课程为期 4 周,每周 8 - 10 小时。
  • Fundamentals of C++ IBM 公司:由 Sathya Ponmalar 等授课,将开启你的 C++ 程序员之旅,通过许多自动评估的 C++ 编码活动,帮助你理解 C++ 的语法和语义,并建立强大的编程和解决问题的技能。本课程为期 5 周,每周 5 - 6 小时。
  • C++ 程序设计西北工业大学:由魏英教授授课,使学员能够使用一种开发工具熟练的进行软件开发,为学员将来的创新实验、毕业设计、科学研究提供有力的技术支持。本课程为期 20 周,每周 6 小时。
  • C++ Programming: Basic Skills Codio 公司:由 Anh Le 授课,教你用 C++ 写一个简单程序所需的基本技能。课程是为没有编码经验的学习者设计的,本课程为期 5 周,每周 2 - 3 小时。
  • Introduction to C++ 微软公司:由 Gerry O'Brien 等授课,介绍 C++ 语言,课程由四个模块组成:C++ 语法、C++ 语言基础、如何在 C++ 中创建函数、为微软公司后续中级和高级 C++ 课程做好准备。本课程为期 4 周,每周 3 - 5 个小时。
  • Object-Oriented Data Structures in C++ 伊利诺伊大学香槟分校:由 Wade Fagen-Ulmschneider 教授授课,将教你如何用 C++ 语言编写程序,包括如何建立一个编写和调试 C++ 代码的开发环境,以及如何将数据结构实现为 C++ 分类。本课程为期 4 周,每周 3 - 7 个小时。

2.官方文档:C++ 的官方文档提供了最准确、最全面的语言规范和指南,是学习与参考的宝贵资源。

3.社区论坛

c++ 博客(A Charmer)

Stack Overflow:解决编程问题的知名社区,在 C++ 编程方面有大量的问题和解答。

Reddit:参与 C++ 社区讨论,获取最新的 C++ 资讯和学习资源。


以上就是本文的全部内容了!

📣​​​​​​​​​​​​如果你想深入了解 C++,欢迎持续关注我【A Charmer】​​​​​​​,我将为你带来更多精彩的编程知识分享。😉


http://www.kler.cn/news/354957.html

相关文章:

  • Kafka之消费者组与消费者
  • YOLOv11改进-模块-引入矩形自校准模块RCM有利于复杂场景(小目标、遮挡等)
  • Python解力扣算法题4(基础)
  • linux中通过一个命令启动任何java的jar包
  • Spring Boot + Vue 前后端分离项目总结:解决 CORS 和 404 问题
  • zipkin启动脚本并指定mysql数据存储
  • 探索桂林:使用SpringBoot构建的旅游平台
  • Leetcode 3320. Count The Number of Winning Sequences
  • 基于SSM+微信小程序的无中介租房系统 (房屋1)
  • 穿越沙漠问题
  • 在线白板:为远程课堂注入活力的协作工具
  • javaweb-xml映射文件编写sql语句
  • 实战RAG第二天——xinference部署大模型,全部代码,保姆级教学
  • 软件测试学习笔记丨Linux三剑客-sed
  • 【实战篇】用SkyWalking排查线上[xxl-job xxl-rpc remoting error]问题
  • 95后研究员4个博士学位 本人发声
  • 如何通过自然外链提升外贸网站权重?
  • Apache SeaTunnel 2.3.8版本正式发布!
  • Jupyter Notebook汉化(中文版)
  • uniapp的移动端骨架屏组件开发应用