必看!用示例代码学 C++ 基础入门,快速掌握基础知识,高效提升编程能力

C++基础入门


目录

C++基础入门

●1.命名空间

●2.输入与输出

●3.缺省参数

●4.函数重载

●5.C++支持函数重载的原理--名字修饰

●6.引用

●7.内联函数

●8.auto关键字(C++11)

●9.基于范围的for循环(C++11)

●10.指针空值nullptr(C++11)


1.命名空间

使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染namespace关键字的出现就是针对这种问题的。一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。

示例:

c++ 复制代码
#include <iostream>
#include <cstdlib>  //rand()
// int rand = 10; error
namespace owndefine
{
    int rand = 10;
}
typedef struct ListNode
{
    struct ListNode *next;
    int data;
} lnode;
namespace function1
{
    typedef struct ListNode
    {
        struct ListNode *next;
        int data;
    } lnode;
}
namespace function2
{
    int Add(int x, int y)
    {
        return x + y;
    }
}
namespace function3
{
    namespace function4
    {
        int Add(int x, int y)
        {
            return x + y;
        }
    }
}
namespace preference_factor
{
    int a = 10;
    int b = 20;
}
int main()
{
    std::cout << "hello world" << std::endl;
 
    //1.示例一:
    // using namespace std;
    // std::cout << rand << std::endl; error
    //   test.cpp:5:5: error: 'int rand' redeclared as different kind of symbol
    //   int rand=10;
    //       ^
    //   In file included from /usr/include/c++/4.8.2/cstdlib:72:0,
    //                   from test.cpp:2:
    //   /usr/include/stdlib.h:374:12: error: previous declaration of 'int rand()'
    //   extern int rand (void) __THROW;
 
    using namespace std;
    //正确解决冲突
    std::cout << owndefine::rand << std::endl;
    std::cout << rand << std::endl;
 
    // 2.示例二:
    lnode *l1 = new lnode();
    cout << "lnode* l1=new lnode()" << endl;
    function1::lnode *l2 = new function1::lnode();
    cout << "function1::lnode* l2=new function1::lnode()" << endl;
 
    // 3.示例三:嵌套命名空间
    cout << function2::Add(1, 2) << endl;
    cout << function3::function4::Add(1, 2) << endl;
 
    // 4.示例四:命名空间参数变量使用
    cout << preference_factor::a << endl;
    cout << preference_factor::b << endl;
    using preference_factor::a;
    cout<<a<<endl;
    using preference_factor::b;
    cout<<b<<endl;
    using namespace preference_factor;
    cout<<a<<endl;
    cout<<b<<endl;
    return 0;
}

2.输入与输出

  1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间使用方法使用std。
  2. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含头文件中。
  3. <<是流插入运算符,>>是流提取运算符.(系统编程部分根据文件的重定向技术做细致了解)
  4. 使用C++ 输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型。(可以这样理解,cout/cin就是两个类,类的内部封装了printf/scanf函数接口,类内并对其进行了相应的优化)
  5. cout和cin分别是ostream和istream类型的对象。

#include库函数中输入输出定义如下:

示例:

c++ 复制代码
#include <iostream>
int main()
{
//命名空间std(namespace std _GLIBCXX_VISIBILITY(default));
//在一般代码练习中我们一般将该命名空间展开,而在项目开发中我们为了避免冲突情况的发生,一般不会展开;
    int data;
    std::cin>>data;
    std::cout<<data<<std::endl;
    return 0;
//其余关于cout/cin的输入输出流操作,结合手册cplusplus进行使用;
}

3.缺省参数

  1. 半缺省参数必须从右往左依次来给出,不能间隔着给;
  2. 缺省参数不能在函数声明和定义中同时出现;
c++ 复制代码
*//a.h
void Func(int a = 10);  //声明
// a.cpp
void Func(int a = 20) //定义
{}
// 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该
用那个缺省值。*
  1. 缺省值必须是常量或者全局变量;
  2. C语言不支持(编译器不支持);

示例:

c++ 复制代码
#include <iostream>
using namespace std;
void function1(int a = 10)
{
    cout << a << endl;
}
void function2(int a = 10, int b = 20, int c = 30)
{
    cout << a << endl;
    cout << b << endl;
    cout << c << endl;
}
void function3(int a, int b = 20, int c = 30)
{
    cout << a << endl;
    cout << b << endl;
    cout << c << endl;
}
int main()
{
    // 示例一
    function1();
    function1(20);
    // 示例二(全缺省)
    function2();
    // 示例三(半缺省)
    function3(10);
    return 0;
}

4.函数重载

函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数/类型/类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

示例:

c++ 复制代码
#include <iostream>
using namespace std;
//参数类型
void Add(int a,int b)
{
    cout<<"void Add(int a,int b)"<<endl;
}
void Add(double a,double b)
{
    cout<<"void Add(double a,double b)"<<endl;
}
//参数个数
void func()
{
    cout<<"void func()"<<endl;
}
void func(int x)
{
    cout<<"void func(int x)"<<endl;
}
//参数顺序
void print_char(int a,char b)
{
    cout<<"void print_char(int a,char b)"<<endl;
}
void print_char(char a,int b)
{
    cout<<"void print_char(char a,int b)"<<endl;
}
int main()
{
    // 示例一
    Add(1,2);
    Add(1.1,2.2);
    // 示例二
    func();
    func(10);
    // 示例三
    print_char(1,'a');
    print_char('a',1);
    return 0;
}

5.C++ 支持函数重载的原理--名字修饰

在C/C++ 中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】

采用C语言编译器编译后结果:

--结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。

采用C++编译器编译后结果:

--结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。

通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分。

6.引用

示例:

c++ 复制代码
#include <iostream>
#include<time.h>
//时钟函数接口:clock();
//size_t start=clock();~size_t end=clock();
using namespace std;
template <class T>
void swap(T &a,T &b)
{
    int tmp=a;
    a=b;
    b=tmp;
}
int& count()
{
    static int cnt=0;
    cnt++;
    //...
    return cnt;
}
int main()
{
    // 示例一
    int a = 10;
    int& ra = a;
    cout << a << endl;
    cout << ra << endl;
 
    // 示例二(应用特性)
    int b = 10;
    // int &rb;//未初始化error
    int& rb = b;
    int& rrb = b;
    cout << rb << endl;
    cout << rrb << endl;
 
    //示例三(常引用)
    const int a=10;
    // int &ra=a;//error,a为常量
    // int &ra=10;//error,10为常量
    const int& ra=a;
    const int& rb=10;
    double d=10.1;
    //int &rd=d;//error,类型错误
    const int& rd=d; //特殊点
    //使用场景
    //1.参数
    int x=10;
    int y=20;
    cout<<"x="<<x<<endl;
    cout<<"y="<<y<<endl;  
    swap(x,y);
    cout<<"x="<<x<<endl;
    cout<<"y="<<y<<endl;  
    //2.做返回值
    int& ret=count();
    cout<<ret<<endl;
    return 0;  
}

传值和传引用的比较:以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。而对于传引用而言,引用的是同一块空间,所以其效率较高。(测试代码如下)

参数和引用的作为返回值类型的性能比较:

值和引用的作为返回值类型的性能比较:

引用和指针的区别:

c++ 复制代码
#include <iostream>
using namespace std;
int main()
{
    int a = 10;
    int &ra = a;
    int *p=&a;
    cout << "&a=" << &a << endl;//0x7ffcfcff1134
    cout << "&ra=" << &ra << endl;//0x7ffcfcff1134
    //引用在其语法概念上,就是起了一个别名而没有独立空间,和其引用实体公用一个空间;
    cout<<"&p="<<&p<<endl;//0x7fffb115edb8
    //而在底层实现上实际上是有空间的,因为引用是按照指针方式来实现的,通过汇编指令可以看出
    return 0;
}
  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求。(引用必须初始化,否者就会报错;而指针可以指向确定空间也可无指向,即空指针)
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体。
  4. 没有NULL引用,但有NULL指针。
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位平台下占8个字节)
  6. 引用自加即引用的实体内容自加,指针自加即指针向后偏移一个类型的大小。
  7. 有多级指针但没有多级引用。
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理。
  9. 引用比指针使用起来相对更安全。(指针所指向开辟在堆上的空间,如果不对其进行释放,则可能会造成内存泄露)

7.内联函数

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数在一定程度上可以提高程序运行的效率。 不是使用了内联函数就会提高程序执行效率,例:一个函数需要调用10000次,使用内联和不使用内联的效率分析。调用函数将会在栈空间中保留一次函数栈帧,函数直接call该栈帧地址即可;若是使用内联函数,则将会在代码中展开10000次调用函数代码,这样的话将会极大地增大程序执行开销。我们的编译器是智能的,即使你写了inline它也将会再合适的时候进行展开,在合适的时候进行调用从而保证程序的执行效率。需要注意的是,inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

8.auto关键字(C++ 11)

C++11引入了auto关键字,用于自动推断变量的类型。auto关键字可以在变量声明时,根据变量的初始值自动确定变量的类型。

示例1:

c++ 复制代码
#include <iostream>
#include <string>
#include <map>
using namespace std;
typedef map<string, string> Map;
typedef map<string, string>::iterator map_iter;
int main()
{
    // auto关键字使用的一些场景
    // 1.类型难于拼写
    // 2.含义不明容易导致错误
    map<string, string> m = {
  {"apple", "苹果"}};
    map<string, string>::iterator m_p = m.begin();
    while (m_p != m.end())
    {
    }
    // 3.使用typedef进行替换
    Map m1 = {
  {"pear", "梨子"}};
    map_iter m_p1 = m1.begin();
    while (m_p1 != m1.end())
    {
    }
    // 4.使用auto
    Map m2 = {
  {"apple", "苹果"}};
    auto m_p2 = m2.begin();
    while (m_p2 != m2.end())
    {
    }
    return 0;
}

示例2:

c++ 复制代码
#include <iostream>
using namespace std;
int TestAuto()
{
    return 10;
}
int main()
{
    int a = 10;
    auto b = a;
    auto c = 'a';
    auto d = TestAuto();
    cout << typeid(b).name() << endl;
    cout << typeid(c).name() << endl;
    cout << typeid(d).name() << endl;
    // auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
    return 0;
}

auto的使用细则:

  1. auto与指针和引用结合起来使用用auto声明指针类型时,用auto和auto没有任何区别,但用auto声明引用类型时则必须加&;
c++ 复制代码
void TestAuto()
{
int x = 10;
auto a = &x;
auto* b = &x;
auto& c = x;  
cout << typeid(a).name() << endl;//Pi int*
cout << typeid(b).name() << endl;//Pi int*
cout << typeid(c).name() << endl;//i int
}
  1. 在同一行定义多个变量当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量;
c++ 复制代码
void TestAuto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}
  1. auto不能作为函数的参数;
c++ 复制代码
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}
  1. auto不能直接用来声明数组;
c++ 复制代码
void TestAuto()
{
int a[] = {1,2,3};
auto b[] = {4,5,6};
}
  1. 为了避免与C++ 98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法;
  2. auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有lambda表达式等进行配合使用;

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

C++11引入了基于范围的for循环(range-based for loop),它是一种更简洁和直观的循环语法,用于遍历容器或数组中的元素。

示例:

c++ 复制代码
#include <iostream>
using namespace std;
int main()
{
    int arr[]={0,1,2,3,4,5,6,7,8,9};
    for(auto e:arr)
        cout<<e<<" ";
    cout<<endl;
    //0 1 2 3 4 5 6 7 8 9
    for(auto &e:arr)
        e*=2;
    for(auto e:arr)
        cout<<e<<" ";
    cout<<endl;
    //0 2 4 6 8 10 12 14 16 18
    //与普通循环类似,也可以使用continue和break
    return 0;
}

10.指针空值nullptr(C++ 11)

C++ 11引入了一个新的关键字nullptr,用于表示空指针。在旧版本的C++中,使用0或者NULL来表示空指针,这种方式可能对于重载函数或者模板有一些歧义。nullptr的引入解决了这个问题。

示例:

c++ 复制代码
#include <iostream>
 
void foo(int* ptr) {
    if (ptr == nullptr) {
        std::cout << "指针为空" << std::endl;
    } else {
        std::cout << "指针不为空" << std::endl;
    }
}
 
int main() {
    int* ptr = nullptr;
    foo(ptr); // 输出:指针为空
 
    int value = 10;
    int* ptr2 = &value;
    foo(ptr2); // 输出:指针不为空
 
    return 0;
}

<您的三连和关注是我最大的动力>
🚀 文章作者:张同学的IT技术日记
分类专栏:C++系列

相关推荐
程序员爱钓鱼10 小时前
Rust 开发环境安装(Windows|macOS|Linux)
后端·rust
爱勇宝18 小时前
第 1 章:别把“需求文档”当成真正的需求
前端·后端·程序员
IT_陈寒1 天前
闭包陷阱让我加了两天班,JavaScript你真行
前端·人工智能·后端
易协同低代码1 天前
通达OA核心类库TD类深度解析
后端
Gopher_HBo1 天前
Go语言学习笔记(十八)Gin处理Session
后端
谭光志1 天前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
她说..1 天前
Java 默认值设置方式
java·开发语言·后端·springboot
foggyprojects1 天前
从0开始,一句话启动AI DataAgent
后端·数据分析·ai编程
郡杰1 天前
一些基础和问题解决
后端
陈随易1 天前
前端项目部署只要30秒
前端·后端·程序员