马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
目次
一 . C++的第一个步伐
二 . 定名空间
2.1 namespace的代价
2.1 namespace 的界说
7.3 定名空间的利用
三 . C++输入&输出
四 . 缺省参数
五 . 函数重载
六 . 引用
6.1 引用的概念和界说
6.2 引用的特性
6.3 引用的利用
6.4 const 引用
6.5 指针和引用的关系(笔试常考)
一 . C++的第一个步伐
C++兼容C语言绝大多数的语法,以是C语言实现的 Hello World! 仍然可以运行 , C++中必要把界说文件代码后缀改为 .cpp , vs 编译器看到的是 .cpp 就会调用C++编译器编译,linux下要用g++编译,不再是gcc
- #include <stdio.h>
- int main()
- {
- printf("Hello World!");
- return 0;
- }
复制代码 固然 , c++也有自己的一套输入输出 , 代码如下 :
- #include<iostream>
- using namespace std;
- int main()
- {
- cout << "Hello World!"<<endl;
- return 0;
- }
复制代码 二 . 定名空间
2.1 namespace的代价
在c++中,变量 、函数 和后续必要学到的类都是大量存在的 , 这些变量 、函数和类的名称都存在于全局作用域中 , 大概会导致许多辩说 。利用定名空间的目的是对标识符的名称举行当地化 , 以克订定名辩说和名字污染 , namespace 关键字的出现就是针对这种题目的 。
c语言项目类似下面步伐如许,出现了定名辩说的题目 , c++ 引入namespace 就是为了更好的办理这类题目 !
全局变量 rand 与 头文件 stdlib.h中 的函数rand 名字发生辩说 , namespace的引入是为了更好的办理这一个题目 。
2.1 namespace 的界说
- 界说定名空间 , 必要利用到namespace 关键字 , 背面跟定名空间的名字 , 然后利用花括号 { } 即可 , { } 里即为定名空间成员。定名空间中可以界说变量/函数/范例等 。
- namespace 本质是界说出一个域 , 这个域跟全局域各自独立 , 差别的域可以界说同名变量 ,以是 上述代码 的rand 不再辩说了 。
- #include <stdio.h>
- #include <stdlib.h>
- namespace bit
- {
- int rand =10 ;
- }
- int main()
- {
- printf("%d\n", bit::rand);
- return 0;
- }
复制代码 这里的 :: 符号 : 定名空间限定符 , 用于指定特定的作用域大概是定名空间 ;比方 bit :: rand 表明 我们必要访问的是bit 定名空间下的rand范例 , 假如没有这个符号,大概导致定名辩说 。
- C++中域有函数局部域 , 全局域 , 定名空间域 , 类域 ; 域影响的是编译时语法查找一个变量/函数/范例出处(声明或界说)的逻辑 , 以是有了域隔离 , 名字辩说就办理了 。局部域和全局域除了会影响编译查找逻辑 , 还会影响变量的生命周期 , 定名空间域和类域不影响变量的生命周期 。
- #include <stdio.h>
- #include <stdlib.h>
- namespace bit
- {
- int rand =10 ;
- }
- int main()
- {
- //这里指的是访问全局的rand函数的地址
- printf("%p\n", rand);
- //这里是访问指定命名空间bit里的rand
- printf("%d\n", bit::rand);
- return 0;
- }
复制代码
- namespace 只能界说在全局 , 固然也可以嵌套界说。
- #include <stdio.h>
- namespace bit
- {
- namespace zhangsan
- {
- int age = 18;
- int add(int x, int y)
- {
- return x + y;
- }
- }
- namespace lisi
- {
- int age = 19;
- int add(int x, int y)
- {
- return x + y;
- }
- }
- }
- int main()
- {
- printf("%d\n", bit::zhangsan::age);
- printf("%d\n", bit::lisi::age);
- printf("%d\n", bit::zhangsan::add(1, 2));
- printf("%d\n", bit::lisi::add(3, 2));
- return 0;
- }
复制代码
- 项目工程中多文件中界说的同名 namespace 会以为是一个namespace , 不会辩说。
stack.h
- #pragma once
- #include<stdio.h>
- #include<stdlib.h>
- #include<stdbool.h>
- #include<assert.h>
- namespace bit
- {
- typedef int STDataType;
- typedef struct Stack
- {
- STDataType * a;
- int top;
- int capacity;
- }ST;
- void STInit(ST * ps, int n);
- void STDestroy(ST * ps);
- void STPush(ST * ps, STDataType x);
- void STPop(ST * ps);
- STDataType STTop(ST * ps);
- int STSize(ST * ps);
- bool STEmpty(ST * ps);
- }
复制代码 stack.c
- #include"Stack.h"
- namespace bit
- {
- void STInit(ST * ps, int n)
- {
- assert(ps);
- ps->a = (STDataType*)malloc(n * sizeof(STDataType));
- ps->top = 0;
- ps->capacity = n;
- }
- // 栈顶
- void STPush(ST * ps, STDataType x)
- {
- assert(ps);
- // 满了, 扩容
- if (ps->top == ps->capacity)
- {
- printf("扩容\n");
- int newcapacity = ps->capacity == 0 ? 4 : ps->capacity* 2;
- STDataType * tmp = (STDataType*)realloc(ps->a,newcapacity * sizeof(STDataType));
- if (tmp == NULL)
- {
- perror("realloc fail");
- return;
- }
-
- ps->a = tmp;
- ps->capacity = newcapacity;
- }
- ps->a[ps->top] = x;
- ps->top++;
- }
- }
复制代码
- c++ 尺度库都放在一个叫std (standard) 的定名空间中 。
7.3 定名空间的利用
编译查找一个变量的声明/界说时 , 默认只会在局部大概全局查找 , 不会到定名空间内里去查找 , 以是下面步伐会编译报错 。 以是我们要利用定名空间中界说的变量/函数,有三种方式 :
1 . 指定定名空间访问 , 在项目中保举这种方式。
2 . using 将定名空间某个成员睁开 , 项目中常常访问的不存在辩说的成员保举这种方式。
3 . 睁开定名空间中全部成员 , 项目不保举 , 辩说风险很大 , 一样平常小训练步伐为了方便保举利用 。
总之就是 , 想利用定名空间的成员就必要告诉编译器,这个变量要到定名空间里找
-----> 1 . 可以用 :: (定名空间限定符)
-----> 2. using 睁开必要利用的定名空间
- #include <stdio.h>
- namespace bit
- {
- int a = 10;
- int b = 20;
- }
- //编译报错:"a"未声明标识符
- int main()
- {
- printf("%d\n", a);
- return 0;
- }
- //指定命名空间访问
- int main()
- {
- printf("%d\n", bit::a);
- return 0;
- }
- //using将命名空间中某个成员展开
- using bit::a;
- int main()
- {
- printf("%d\n", a);
- printf("%d\n", bit::b);
- return 0;
- }
- //展开命名空间中全部成员
- using namespace bit;
- int main()
- {
- printf("%d\n", a);
- printf("%d\n", b);
- return 0;
- }
复制代码 三 . C++输入&输出
- <iostream> 是 Input Output Stream 的缩写 , 是尺度的输入,输出流库 , 界说了尺度的输入,输出对象。
- std::cin 是istream 类的对象 , 它告急面向窄字符 ( narrow characters (of type char)) 的尺度输入流 。
- std:
ut 是 ostream 类的对象 , 它告急面向窄字符的尺度输出流。
- std::endl 是一个函数 , 流插入输出时 , 相称于插入一个换行字符 + 革新缓冲区。
- << 是流插入运算符 , >> 是流提取运算符 。(c语言还用这两个运算符做位运算左移/右移)
- 利用c++ 输入输出更方便 , 不必要像print / scanf 输入输出时那样 , 必要手动指定格式 , c++ 的输入输出可以主动辨认变量范例 ( 本质是通过函数重载实现) , 告急的是 , c++流能更好的支持自界说范例对象的输入输出。
- IO流涉及类和对象 , 运算符重载 , 继承等许多面向对对象的知识
- cout / cin / endl 等都属于c++ 尺度库 , c++ 尺度库都放在一个叫 std ( standard ) 定名空间中 , 以是必要通过定名空间的利用去利用他们 ---------------> using namespace std;
一样平常训练中 , 我们可以利用 using namespace std , 但是在现实项目开辟中 , 不发起利用。
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<iostream>
- using namespace std;
- int main()
- {
- int a = 0;
- double b = 0.1;
- char c = 'a';
- cout << a << " " << b << " " << c << endl;
- std::cout << a << " " << b << " " << c <<std:: endl;
- //没有包含<stdio.h>,也可以使用print和scanf,在包含<iostream>间接包含了
- //vs编译器是这样的,其他编译器可能会报错
- scanf("%d%lf", &a, &b);
- printf("%d %lf\n", a, b);
- //可以自动识别类型
- cin >> a;
- cin >> b >> c;
- cout << a << endl;
- cout << b << " " << c << endl;
- return 0;
- }
复制代码 在io需求比力高的地方,如部门⼤大量输入的比赛题中,加上以下3行代码,可以进步C++IO服从
- #include<iostream>
- using namespace std;
- int main()
- {
- // 在io需求⽐较⾼的地⽅,如部分⼤量输⼊的竞赛题中,加上以下3⾏代码
- // 可以提⾼C++IO效率
- ios_base::sync_with_stdio(false);
- cin.tie(nullptr);
- cout.tie(nullptr);
- return 0;
- }
复制代码 四 . 缺省参数
1 . 缺省参数是声明或界说函数时为函数的参数指定一个缺省值。在调用该函数时,假如没有指定实参则接纳该形参的缺省值,否则利用指定的实参,缺省参数分为全缺省和半缺省参数。(有些地方把缺省参数也叫默认参数)
2 . 全缺省就是全部形参给缺省值 , 半缺省就是部门形参给缺省值 。 C++ 规定半缺省参数必须从右往左依次连续缺省 , 不能隔断跳跃给缺省值 。
3 . 带缺省参数的函数调用 , C++规定必须从左到右依次给实参 , 不能跳跃给实参 。
4 . 函数声明和界说分离时 , 缺省参数不能在函数声明和界说中同时出现 , 规定必须函数声明给缺省值 。
- #include<iostream>
- using namespace std;
- //全缺省
- void Func1(int a = 10,int b = 20,int c=30)
- {
- cout << "a = " << a << endl;
- cout << "b = " << b << endl;
- cout << "c = " << c << endl << endl;
- }
- //半缺省
- void Func2(int a, int b = 10, int c = 20)
- {
- cout << "a = " << a << endl;
- cout << "b = " << b << endl;
- cout << "c = " << c << endl << endl;
- }
- int main()
- {
- Func1();
- Func1(1);
- Func1(1,2);
- Func1(1,2,3);
- Func2(100);
- Func2(100,200);
- Func2(100,200,300);
- return 0;
- }
复制代码 五 . 函数重载
C++支持在同一作用域出现同名函数 , 但是要求这些同名函数的形参差别 , 可以是参数个数差别大概范例差别 。 如许C++函数调用就表现出了多态活动,利用更机动 。 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;
- }
- void f1()
- {
- cout << "f()" << endl;
- }
- void f1(int a = 10)
- {
- cout << "f(int a)" << endl;
- }
- int main()
- {
- Add(10, 20);
- Add(10.1, 20.2);
- f();
- f(10);
- f(10, 'a');
- f('a', 10);
- return 0;
- }
复制代码 返回值差别不能作为重载条件 , 由于调用时也无法区分 , 会报错 , 存在歧义 , 编译不知道调用谁 !
- void fxx()
- {
- }
- int fxx()
- {
- return 0;
- }
复制代码
六 . 引用
6.1 引用的概念和界说
引用不是新界说一个变量 , 而是给已存在变量取了一个别名 , 编译器不会为引用变量开辟内存空间 , 它和它引用的变量共用同一块内存空间 。
格式 :
范例& 引用别名 = 引用对象 ;
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<iostream>
- using namespace std;
- int main()
- {
- int a = 0;
-
- //引用 : b 和 c 是 a 的别名
- int& b = a;
- int& c = a;
- //也可以给别名取别名
- int& d = b;
- ++d;
- //指向同一块的空间
- cout << &a << endl;
- cout << &b << endl;
- cout << &c << endl;
- cout << &d << endl;
- return 0;
- }
复制代码 这里引用可以拿水浒传中的李逵作比 , 宋江叫他“铁牛” , 江湖人称 " 黑旋风“ ;铁牛,李逵,黑旋风都是同一个人 ;
C++ 中为了克制引入太多的运算符 , 会复用C 的一些符号 ,比如前面的 << 和 >> , 这里引用 也和 取所在 利用了同一个符号 & , 各人留意区分
6.2 引用的特性
- 引用在界说时 , 必须初始化
- 一个变量可以有多个引用
- 引用一旦引用一个实体 , 就不能再引用其他实体
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<iostream>
- using namespace std;
- int main()
- {
- int a = 0;
-
- int& b = a;
- int& c = a;
- int& d = b;
- int e = 10;
- b = e;
- cout << a << endl;
- cout << b << endl;
- cout << c << endl;
- cout << d << endl;
- cout << e << endl;
- cout << &a << endl;
- cout << &b << endl;
- cout << &c << endl;
- cout << &d << endl;
- cout << &e << endl << endl;
- return 0;
- }
复制代码 6.3 引用的利用
1 . 引用在实践中告急是用于引用传参 和 引用返回值中淘汰拷贝进步服从 和 改变引用对象时同时改变被引用对象 。
2 . 引用传参跟指针传参功能是类似的,引用传参相对更方便一些。
3 . 引用返回值的场景相对比力复杂 , 这里简单先容以了局景 , 在后续的类和对象的博客中,还会继承深入先容 。
4 . 引用 和 指针 在实践中相辅相成,功能有重叠性 , 但是各有特点 , 相互不可更换 。 C++的引用跟其他语言的引用 ( 如Java ) 是有很大的区别的 , 除了用法 , 最大的点 , C++引用界说后不能改变指向 , Java 引用可以改变指向 。
1 . 下面举一个我们熟悉的交换函数Swap , 来领会以下 引用 的 ”巧“ :
指针传参版本 :
- #include<iostream>
- using namespace std;
- void Swap(int* x, int* y)
- {
- int tmp = *x;
- *x = *y;
- *y = tmp;
- }
- int main()
- {
- int x = 1;
- int y = 2;
- cout << "交换之前:x = " << x << " y = " << y << endl;
- Swap(&x, &y);
- cout << "交换之后:x = " << x << " y = " << y << endl;
- return 0;
- }
复制代码 引用传参版本:
- void Swap(int& x, int& y)
- {
- int tmp = x;
- x = y;
- y = tmp;
- }
- int main()
- {
- int x = 1;
- int y = 2;
- cout << "交换之前:x = " << x << " y = " << y << endl;
- Swap(x, y);
- cout << "交换之后:x = " << x << " y = " << y << endl;
- return 0;
- }
复制代码
2 . 紧接着再来看看之前实现过的数据结构 -- 栈 , 利用引用传参 :
1 ) 引用后 , 形到场实参指向的空间是同一个 , 传参的时间不必要转达所在
2 ) 访问结构体的时间 不利用 -> 操纵符 , 而利用 . 操纵符
假如想利用引用传参来实现数据结构栈 , 可以看之前博客 : 栈与队列的常见接口的实现-CSDN博客 , 试着把指针传参改为引用传参
- #include <assert.h>
- #include<iostream>
- using namespace std;
- typedef int STDataType;
- typedef struct Stack
- {
- STDataType* a;
- int top;
- int capacity;
- }ST;
- void STInit(ST& rs, int n = 4)
- {
- rs.a = (STDataType*)malloc(n * sizeof(STDataType));
- rs.top = 0;
- rs.capacity = n;
- }
- // 栈顶
- void STPush(ST & rs, STDataType x)
- {
- // 满了, 扩容
- if (rs.top == rs.capacity)
- {
- printf("扩容\n");
- int newcapacity = rs.capacity == 0 ? 4 : rs.capacity * 2;
- STDataType * tmp = (STDataType*)realloc(rs.a, newcapacity *sizeof(STDataType));
- if (tmp == NULL)
- {
- perror("realloc fail");
- return;
- }
- rs.a = tmp;
- rs.capacity = newcapacity;
- }
- rs.a[rs.top] = x;
- rs.top++;
- }
- // int STTop(ST& rs)
- int& STTop(ST & rs)
- {
- assert(rs.top > 0);
- return rs.a[rs.top];
- }
- int main()
- {
- // 调⽤全局的
- ST st1;
- STInit(st1);
- STPush(st1, 1);
- STPush(st1, 2);
- cout << STTop(st1) << endl;
-
- STTop(st1) += 10;
- cout << STTop(st1) << endl;
- return 0;
- }
复制代码 3 . 一些告急用C代码实现版本数据结构的课本中 , 会利用C++引用传参 , 来代替指针传参 , 目的是简化步伐 , 避开复杂指针 ---> 一级指针 , 二级指针 ...;
- //使用引用来代替一级指针
- typedef struct SeqList
- {
- int a[10];
- int size;
- }SLT;
- // ⼀些主要⽤C代码实现版本数据结构教材中,使⽤C++引⽤替代指针传参,⽬的是简化程序,避开复杂的指针
- void SeqPushBack(SLT& sl, int x)
- {
- }
- //void SeqPushBack(SLT* sl, int x);
- //void SeqPushBack(SLT& sl, int x)
复制代码- //使用引用来替代二级指针
- //void ListPushBack(LTNode** phead, int x)
- //void ListPushBack(LTNode*& phead, int x)
- #include<iostream>
- using namespace std;
- typedef struct ListNode
- {
- int val;
- struct ListNode* next;
- }LTNode, *PNode;
- // 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名
- // 这样就不需要⽤⼆级指针了,相对⽽⾔简化了程序
- void ListPushBack(PNode& phead, int x)
- {
- PNode newnode = (PNode)malloc(sizeof(LTNode));
- newnode->val = x;
- newnode->next = NULL;
- if (phead == NULL)
- {
- phead = newnode;
- }
- else
- {
- //...
- }
- }
- int main()
- {
- PNode plist = NULL;
- ListPushBack(plist, 1);
- return 0;
- }
复制代码 留意 : 这里的Pnode 是结构体指针变量 。对于二级指针引用 ,拿 int 型做比, 只能是
----> int* & 引用名 = 引用对象 ;( 为整型指针变量给别名)
不会有 int & * 引用名 = 引用对象 ,如许的界说,不符合引用的格式
4 . 引用返回值
下面的STTop(S) += 10 , 会报错 ,由于函数返回的时间 , 返回的是临时对象 , 具有常性 , 不可以更改 !
假如想要实现 STTop(S) += 10 , 利用引用 , 返回对象的别名
为什么必要有临时对象的概念?直接返回不可吗?
----> 下面的代码 , top 假如出了作用域 , 生命周期竣事了,不利用 临时对象/寄存器存储 , top 就找不到了
6.4 const 引用
1 . 可以引用一个const 对象 , 但是必须用 const 引用 。 const 引用也可以引用平凡对象,由于对象的访问权限在引用过程中可以缩小 , 但是不能放大 。
2 . 必要留意的是 类似 :
int & rb = a*3 ; double d = 12.34 ; int& rd = d ;
如许一些场景下 a* 3的结果生存在一个临时对象中 , int& rd = d 也是类似 , 在范例转换中会产生临时对象存储中心值 , 也就是 , rb 和 rd 引用的都是临时对象 , 而 C++ 规定临时对象具有常性 , 以是这里就触发了权限放大 , 必须要用常引用才可以 。
3 . 所谓临时对象就是编译器必要一个空间暂存表达式的求值结果时 -----> 临时创建的一个未定名的对象 , C++中把这个未定名的对象叫做临时对象 。
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<iostream>
- using namespace std;
- int main()
- {
- const int a = 10;
- //权限不可以放大
- //int& ra = a;
- //权限可以平移
- const int& ra = a;
- //权限可以缩小
- int b = 20;
- const int& rb = b;
- return 0;
- }
复制代码
- //引用传参,减少拷贝,建议用引用传参,好处是实参可以是常量/运算表达式等
- void Func(const int& r)
- {
- }
- int main()
- {
- int a = 10;
- const int& ra = a;
- const int& rra = 30;
- const int& rb = a * 3;
- double d = 13.14;
- const int& rd = d;
- Func(a);
- Func(10);
- Func(a * 3);
- Func(d);
- return 0;
- }
复制代码 6.5 指针和引用的关系(笔试常考)
C++中指针和引用就像两个性格迥异的亲兄弟 , 指针是哥哥 , 引用是弟弟 , 在实践中 , 他们相辅相成 , 功能具有重叠性 , 但是各有自己的特点 ,相互不可以更换 。
- 语法概念上 : 引用是一个变量的取别名 ---> 不开空间 , 指针是存储一个变量的所在 , 必要开辟空间 。
- 引用在界说时必须初始化,指针发起初始化,但是在语法上不是必须的。
- 引用初始化时引用一个对象 , 就不能再引用其他对象 ; 而指针可以在不绝地改变指向对象 。
- 引用可以直接访问指向对象 , 指针必要解引用才气访问指向对象。
- sizeof 中寄义差别 , 引用结果为引用范例的巨细 , 但指针始终是所在空间所占字节个数(32位平台下 --> 4个字节 , 64 位平台下 8 字节)
- 指针很轻易出现空指针和野指针的题目 , 引用很少出现 , 引用利用起来相对更安全一些 。
|