IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++基础 -> 正文阅读

[C++知识库]c++基础

命名空间

概念

命名空间是一个声明性区域,为其内部的标识符(类型、函数和变量等的名称)提供一个范围。

解释

在理解命名空间之前,我们先看一下这个代码

#include<stdio.h>
#include<stdlib.h>
int rand=0;
int main()
{
    printf("%d\n",rand);
    return 0;
}

这个代码在运行时会报错:"rand"重定义

为什么会报错?

在预处理时,会进行头文件展开,在stdlib头文件种包含rand()函数,rand()函数属于全局范围,在全局范围中还有一个全局变量rand,在同一域内有多个同一变量,在这种情况下就会发生命名冲突

如何解决?

C++中引入了命名空间

命名空间定义

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。

注意:

1、命名空间只能在全局定义

2、在命名空间内的变量还是全局变量,生命周期不变

定义命名空间的三种方式

//1、普通的命名空间
namespace byld //byld为命名空间的名称
{
    //命名空间中的内容,既可以定义变量,也可以定义函数
    int rand=0;//将rand关到一个空间
}
//2、命名空间可以嵌套
namespace byte
{
    int a=10;
    namespace data
    {
        int b=20;
    }
}
//3、同一个工程中允许存在多个相同名称的命名空间,编译器在编译时会合成同一个命名空间中。
//可能会有冲突(嵌套命名空间解决)(?)
namespace byld
{
    int d=30;
}

命名空间使用

如何使用命名空间?

使用::(域作用限定符)

namespace byld
{
	int a = 20;
}
int a = 30;
int main()
{
	printf("%d\n", ::a);//访问全局变量
	printf("%d\n", byld::a);//访问byld域内的a变量
	return 0;
}

指定命名空间麻烦?

两种解决方式

1、
using namespace byte;//把byte这个命名空间定义的东西放出来
2、
using namspace bit::f;//只放f出来

比如:

namespace byld
{
    int a = 30;
}
namespace byte
{
    int b = 20;
    int c = 10;
}
using namespace byld;
using byte::b;
int main()
{
    printf("%d\n", a);//30
    printf("%d\n", b);//20
    printf("%d\n", byte::c);//10
}

注意:

1、如果是嵌套命名空间,要展开内部的命名空间,需要先展开外部的命名空间

比如:

namespace byte
{
    int a = 10;
    namespace data
    {
        int b = 20;
    }
}
using namespace byte;
using namespace data;
int main()
{
    printf("%d\n", b);//打印20
    return 0;
}

2、打开命名空间后,依旧可以采用命名空间名::变量的形式访问

namespace byld
{
    int a = 30;
}
using namespace byld;
int main()
{
    printf("%d\n", byld::a);//打印30
}

3、如果打开命名空间,有可能会导致命名冲突

比如:

namespace byld
{
    int a = 30;
}
namespace byte
{
    int a = 10;
    namespace data
    {
        int b = 20;
    }
}
using namespace byte;
using namespace byld;
int main()
{
    printf("%d\n", byte::a);//打印10
    printf("%d\n", byld::a);//打印30
    printf("%d\n", a);//错误,命名冲突
    return 0;
}

C++输入&输出

首先,我们先认识几个运算符

//流提取运算符
cin
//流插入运算符
cout
//换行
endl =='\n'(endl就相当于'\n')

举例

#include<iostream>
using namespace std;/std是封c++标准库的命名空间
int main()
{
    cout<<"Hello World!"<<endl;
    return 0;
}

说明:

1、 使用 cout 标准输出(控制台)和 cin 标准输入(键盘)时,必须包含 < iostream > 头文件以及std标准命名空间。

注意:早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应头文件 即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,规定C++头文 件不带.h;旧编译器(vc 6.0)中还支持格式,后续编译器已不支持,因此推荐使用 + std 的方式。

2、使用C++输入输出更方便,不需增加数据格式控制,比如:整形–%d,字符–%c

#include <iostream>
using namespace std;

int main()
{
    int a;
    double b;
    char c;
    cin>>a;
    cin>>b>>c;
    cout<<a<<endl;
    cout<<b<<" "<<c<<endl;
    return 0;
}

缺省函数

全缺省参数

void TestFunc(int a = 10, int b = 20, int c = 30)
{
 cout<<"a = "<<a<<endl;
 cout<<"b = "<<b<<endl;
 cout<<"c = "<<c<<endl;
}

半缺省参数

void TestFunc(int a, int b = 10, int c = 20)
{
 cout<<"a = "<<a<<endl;
 cout<<"b = "<<b<<endl;
 cout<<"c = "<<c<<endl;
}

注意:

1、半缺省参数必须从右往左缺省,并且连续

2、缺省参数不能在函数声明和定义中同时出现

缺省函数声明给,定义给(错误)

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

4、C语言不支持

函数重载

概念

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

注意:

返回值不同是无法构成重载的(调用时无法区分调用的是谁?)

比如:

int Add(int left, int right)
{
    return left+right;
}
double Add(double left, double right)
{
    return left+right;
}
long Add(long left, long right)
{
    return left+right;
}
int main()
{
    Add(10, 20);
    Add(10.0, 20.0);
    Add(10L, 20L);
    
    return 0;
}

问:下面两个函数属于函数重载吗?

short Add(short left, short right)
{
    return left+right;
}
int Add(short left, short right)
{
    return left+right;
}

名字修饰

为什么C++支持函数重载?而C语言不支持函数重载?

这个问题,我们需要了解在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理,编译,汇编,链接

编译链接的过程:
f.h f.cpp test.cpp

1、预处理 – 头文件的展开、宏替换、条件编译、去掉注释

f.i test.i

2、编译 – 检查语法,生成汇编代码(语法错误)

f.s test.s

3、汇编 – 把汇编代码翻译成二进制的机器码

f.o test.o

4、链接 – 找调用函数的地址,链接对应上,合并到一起

f.o,函数调用指令,符号表(函数名,函数地址的映射)

链接错误:在函数表中找不到对应的

为什么C语言不支持?

因为在符号表中,直接用的是函数名,所以如果采用重载函数的话会导致冲突

为什么C++支持?

因为在函数表中,采用的是经过修饰的函数名

函数名修饰:_Z函数名长度 函数名 类型首字母(这也是为什么重载函数不能只以返回回值类型不同)

extern"C"

C 可以调用C/CPP静态库/动态库

CPP 可以调用C/CPP静态库/动态库

C 调用CPP静态库/动态库

CPP 调用C静态库/动态库

在这两种情况下,不能直接调,会找不到(C直接按函数名找,C++会按修饰过的函数名找)

1、CPP如何调用C静态库/动态库时?

1、链接Stack_C.lib

2、

extern "C"
{ 
    #include"../Stack_C/Stack.h"
}
1. stack.h的内容会在{}内展开
    extern "C"表示在{}内的函数在链接时,以C的方式链接(按C的方式生成符号表,按C的方式查找)

2、C如何调用CPP静态库/动态库

1、链接Stack_CPP.lib(在Stack_CPP中不能使用重载函数)

2、

//条件编译
#ifdef __cplusplus
#define EXTERN_C extern"C"
#else
#define EXTERN_C extern"C"
#endif 

typedef int STDataType;
typedef struct Stack
{
	STDataType* val;
	int top;
	int capacity;
}Stack;

EXTERN_C void StackIniti(Stack* ps);

EXTERN_C void StackPush(Stack* ps, STDataType data);
// 出栈
EXTERN_C void StackPop(Stack* ps);
// 获取栈顶元素
EXTERN_C STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
EXTERN_C int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
EXTERN_C bool StackEmpty(Stack* ps);
// 销毁栈
EXTERN_C void StackDestroy(Stack* ps);

引用

概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

类型&引用变量名(对象名)=引用实体

void Test()
{
    int a = 10;
    int& ra = a;
    //&a==&ra
    printf("%p\n", &a);
    printf("%p\n", &ra);
}

注意:引用类型必须和引用实体是同中类型的

引用特性

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

2、一个变量可以有多个引用

3、一个引用一旦引用一个实体,就不能引用其他实体

常引用

在常引用中,涉及到取别名原则

对原引用变量,权限只能缩小,不能放大

比如:

int main()
{

    const int x=10;
    int& y = x;//放大(错误)
    const int& y=x;//不变
    
    int c=30;
    const int& d=c;//缩小
    
    //对常量取别名
    int &a=10;(错误)
    const int&c=20;(正确)
        
    return 0;
}

接下来看如下代码:

问:const int& e=d为什么正确

int main()
{      
    double d=2.2;
    int f=d;//隐式类型转换(临时变量)
    //在隐式转换中,会截取double的整数部分给到中间变量,中间变量再给到f
    const int& e=d;//e是d的临时变量的别名,这里需要用常引用接受,因为临时变量具有常性
    return 0;
}

由谁充当临时变量

1、寄存器(小)
2、(例)在main栈区内开一块空间(大)

引用使用场景

1、 做参数

void Swap(int& left, int& right)
{
    int temp = left;
    left = right;
    right = temp;
}

2、作返回值

在了解引用做返回值之前,我们需要知道函数返回的是一个临时变量,在函数名前的返回值类型临时变量的类型

比如:

//1、返回值不是引用
int add(int a,int b)
{
    int n = a + b;
    return n;
}//在add函数结束后,n会被销毁,所以会把n的内容拷贝给临时变量,实际上返回的是临时变量

//2、返回值是引用
int& mult(int a,int b)
{
    int c= a * b;
    return c;
}//mult函数的返回值类型为int&,返回c的引用,不会发生拷贝,但是在mult函数结束后,c会被销毁,在主函数中调用,会形成非法访问。所以不能在这种情况使用引用作返回值。

int main()
{
    int a=1,int b=2;
    printf("%d\n",add(1,2));
    printf("%d\n",mult(1,2));//非法访问
    return 0;
}

注意:如果函数返回时,出了函数作用域,如果返回对象还在,可以用引用返回(比如static修饰的变量)

引用和指针

int main()
{
    int a=10;
    //语法角度:ra是a的别名,没有额外开空间
    //底层角度:他们是一样的方式实现的
    int& ra=a;
    ra=20;
    //语法角度:pa存储a的地址,pa开了4/8byte空间
    //底层角度:他们是一样的方式实现的(转化成一样的汇编代码)
    int* pa=a;
    *pa=20;
    
    return 0;
}

内联函数

概念

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销, 内联函数提升程序运行的效率。

为什么C++要出inline?

1、解决宏晦涩难懂,容易写错

2、宏不支持调试

注意:

debug不展开;release展开

内联函数优点:

1、debug支持调试

2、不容易写错

特性

1、inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜 使用作为内联函数。

2、inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等,编译器优化时会忽略掉内联。

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

auto关键字

简介

C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型 指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

auto使用

1、

#include<iostream>
using namespace std;

int TestAuto()
{
    return 10;
}
int main()
{
    int a=10;
    auto b=&a;//auto根据=右的类型推导出b的类型(自动推导)
    auto c='a';
    auto d=TestAuto();

    //typeid(b).name() b变量的类型
    cout<<typeid(b).name()<<endl;//打印int*
    cout<<typeid(c).name()<<endl;//打印char
    cout<<typeid(d).name()<<endl;//打印int
    return 0;
}

2、auto与指针和引用结合起来使用 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

int main()
{
    int x=10;
    auto a=&x;
    auto* b=&x;
    auto* d=x;//错误,d已经明确类型是指针,x必须为地址
    auto& c =x;
    cout<<typeid(b).name()<<endl;//打印变量的类型
    cout<<typeid(c).name()<<endl;   
    cout<<typeid(d).name()<<endl;
    
    *a=20;
    *b=30;
    c=40;
    
    return 0;
}

3、在同一行定义多个变量 ,当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

int main()
{
    int x=10,y=20;
    double z=3.14;
    auto a=x,b=y;//正确
    auto c=x,z=3.14;//错误
    
    return 0;
}

auto使用注意事项:

2、auto不能做参数

3、auto必须初始化

4、auto不能做返回值

5、auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有lambda表达式等进行配合使用。

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

简介

C++11中 引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量, 第二部分则表示被迭代的范围。

比如:

void TestFor()
{
    int array[] = { 1, 2, 3, 4, 5 };
    for(auto& e : array)
        e *= 2;
    for(auto e : array)
        cout << e << " ";//打印2 4 6 8 10
 
    return 0;
}

nullptr(关键字)

空指针有三种表达方式

int main()
{
    int*a=NULL;//1
    int*b=0;//2
    int*c=nullptr;//3
    return 0;
}

在这三种方式中,1,2是不规范的,因为NULL是一个宏,本质上和0是一样的

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

如果使用不规范的写法就会导致与本意不服:

void f(int)
{
    cout<<"f(int)"<<endl;
}
void f(int*)
{
    cout<<"f(int*)"<<endl;
}
int main()
{
    f(0);//打印f(int)
    f(NULL);打印f(int)
    f(int*(NULL));//打印f(int*)
    f(nullptr);//打印f(int*)
    
    return 0;
}

注意:

1、在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。

2、在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。

3、 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-26 15:13:27  更:2022-05-26 15:13:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/13 20:30:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码