C++初阶C++入门
Posted Huang_ZhenSheng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++初阶C++入门相关的知识,希望对你有一定的参考价值。
目录
命名空间
首先,我们看下这段代码:
#include<iostream>//头文件
using namespace std;//命名空间
int main()
{
cout << "hello world" << endl;//IO输出流
return 0;
}
思考:使用using namespace std的作用是:?
这里暂且放一下,我们先来学习一下命名空间
命名空间定义:
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
C语言中
同一个作用域不能定义同名的
同时如果跟库的命名冲突了,C语言也无法解决
#include<stdio.h>
int a = 0;
//int a = 0;
//同一个作用域不能定义同名的
int main()
{
int a = 1;
return 0;
}
//跟库的命名冲突了,C语言无法解决
#include<stdio.h>
#include<stdlib.h>//报错—>会发生冲突
int a = 0;
int rand = 10;
int main()
{
int a = 1;
printf("%d",rand);
return 0;
}
因此,C++提出了命名空间来解决名字冲突的问题,于是namespace诞生了
#include<stdio.h>
#include<stdlib.h>
int a = 0;
namespace hzs//可以在命名空间里面定义变量
{
int rand = 10;
}
int main()
{
int a = 1;
//printf("%d", rand);//访问全局的,打印出一个函数指针
//如何处理?
printf("%d",hzs::rand);//::域作用限定符,rand在左边bit的域里
return 0;
}
注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
除了定义变量,还有定义函数,定义类型,结构体,命名空间还可以嵌套定义
同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中
//除了命名空间,还可以定义函数
#include<stdio.h>
#include<stdlib.h>
namespace hzs//可以在命名空间里面定义变量
{
//定义变量,定义函数,定义类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
//还可以定义结构体
struct Node
{
struct Node*next;
int val;
};
//命名空间还可以嵌套定义
namespace hzs2
{
int c;
int d;
int Sub(int left, int right)
{
return left - right;
}
}
}
//有了命名空间,他们互相之间就可以命名同名的东西
namespace cpp
{
int rand = 100;
}
int main()
{
int a = 1;
printf("%d\\n", rand);//访问全局的,打印出一个函数指针
//如何处理?
printf("%d\\n",hzs::rand);//::域作用限定符,rand在左边bit的域里
printf("%d\\n", cpp::rand);
hzs::Add(1, 2);
struct hzs::Node node;
hzs::hzs2::Sub(1, 2);
return 0;
}
命名空间的使用:
如果使用命名空间里面的东西呢?(具体的说明在注释里!)
//如何使用命名空间里面的东西?
//三种方式
//1,全部直接展开到全局
using namespace std;//std是包含C++标准库的命名空间,优点:用起来方便,缺点:把自己的定义暴露出去,导致命名污染
using namespace hzs;
int main()
{
Add(1, 2);
return 0;
}
//2,访问每个命名空间中的东西时,制定命名空间
//优点:不存在命名污染,缺点,用起来麻烦,每个都得去指定命名空间
std::rand
//3,把某个展开—>可以把常用的展开,不会造成大面积的污染
using hzs::Node;
using hzs::Add;
int main()
{
struct Node n1;
int ret = Add(1, 2);
printf("%d",ret);
hzs::rand = 100;
return 0;
}
所以,这里就可以回到第一个问题的答案:
#include<iostream>
using namespace std;
int main()
{
cout << "hello" << endl;
return 0;
}
C++输入和输出
使用cout标准输出(控制台)和cin标准输入(键盘)时,必须包含<iostream >头文件及std标准命名空间。
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;//>>输入运算符/流提取运算符
int* a = (int*)malloc(sizeof(int)*n);
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
for (int i = 0; i < n; i++)
{
cout << a[i]<<" ";//<<输出运算符/流插入运算符
}
return 0;
}
自动识别类型:使用C++输入输出更方便,不需增加数据格式控制,比如:整形--%d,字符--%c
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;//>>输入运算符/流提取运算符
double* a = (double*)malloc(sizeof(double)*n);
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";//<<输出运算符/流插入运算符
}
return 0;
}
#include<iostream>
using namespace std;
//自动识别类型
struct Student
{
char name[10];
int age;
};
int main()
{
char ch = 'A';
int i = 10;
int* p = &i;
double d = 1.11111;//最多输出五位
//自动识别变量的类型
cout << ch << endl;
cout << i << endl;
cout << p << endl;
cout << d << endl;
printf("%.2f\\n",d);
//类似下面的场景使用printf更好用
struct Student s = { "张三",18 };
cout << "名字:" << s.name << " " << "年龄:" << s.age << endl;
printf("名字:%s 年龄:%d\\n",s.name,s.age);
return 0;
}
缺省参数
缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。
全缺省:
半缺省:
1. 半缺省参数必须从右往左依次来给出,不能间隔着给
2. 缺省参数不能在函数声明和定义中同时出现
函数重载
函数重载的概念:
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题
为什么C++支持函数重载,而C语言不支持函数重载呢?
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
C语言为什么不支持函数重载呢?
C编译器,直接用函数名关联,函数名相同时,它无法区分
下面我们来看一下C的汇编代码:
不难看出,其Addi和Add的汇编代码一致
那么C++如何支持函数重载呢
函数名修饰规则——>不能直接用函数名,要对函数名进行修饰,带入参数特点修饰
函数名相同,只要参数不同,修饰出来名字就不同,那么就能区分,就支持重载
extern “C”:
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern "C",意思是告诉编译器,将该函数按照C语言规则来编译。比如:tcmalloc是google用C++实现的一个项目,他提供tcmallc()和tcfree两个接口来使用,但如果是C项目就没办法使用,那么他就使用extern “C”来解决。
引用
引用概念:
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
类型& 引用变量名(对象名) = 引用实体
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
//注意:这里跟C取地址用了一个符号&
//之间无关联,各作各的用处
int a = 10;
int& b = a;
int& c = a;
int& d = b;
return 0;
}
注意:引用类型必须和引用实体是同种类型的
引用特性:
1.引用在定义的时候必须初始化
//错误:int& e;
2.一个变量可以有多个引用
3.引用一丹引用一个实体,再不能引用其他实体
//错误
int main()
{
int a = 10;
int& b = a;
int c = 20;
b = c;
}
常引用:
int main()
{
//const引用
const int a = 10;
//int& b = a;//变成你的别名,还能修改你(不行)
int c = 20;
const int& d = c;//变成你的别名,不能修改你(行)
return 0;
}
引用和指针的区别:
下面这段代码执行后,结果是什么样子的?
int main()
{
int x = 0;
int y = 1;
int*p1 = &x;
int*p2 = &y;
int*&p3 = p1;
*p3 = 10;
p3 = p2;
return 0;
}
通过调式可以发现, 一开始p1,p3存的是x的地址,经过P3=P2,把P2的地址给了P3,P1
使用场景:
1.做参数:
2.做返回值:
传值返回 :
#include<iostream>
using namespace std;
int Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int ret = Add(1, 2);
Add(3, 4);
cout << "Add(1,2) is:" << ret << endl;
return 0;
}
调用Add函数的时候,1+2的结果给了C,return c好像就是把c作为返回值,拷贝给ret
实际上不是这样的,实际上return c不会用c去做这里的返回值,因为c出了作用域就不在了
—>编译器产生一个临时变量,临时变量去做返回值,传值返回(会有一次拷贝)
这个临时变量如果比较小,通常是寄存器,如果比较大,通常会在main函数中开一块临时空间
证明一下上述说产生临时变量的说法:
如果将代码换成这样,会编译不通过
int& ret = Add(1, 2);
如果换成这样,编译通过 (利用临时变量具有常性)
const int& ret = Add(1, 2);
传引用返回:
#include<iostream>
using namespace std;
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = Add(1, 2);
Add(3, 4);
cout << "Add(1,2) is:" << ret << endl;
return 0;
}
为什么这里的结果是 7 ???
我们先来看一段代码:
传引用返回:
#include<iostream>
using namespace std;
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int ret = Add(1, 2);
Add(3, 4);
cout << "Add(1,2) is:" << ret << endl;
return 0;
}
传引用返回结果不确定,取决于平台销毁栈祯时是否会清理栈祯空间
如果清理了就是随机值,如果不清理,给ret的就是3
运行程序,可以发现VS上没有清理栈祯,我们进一步改动程序验证引用返回的危害性
小结:传值返回有拷贝,传引用返回没有拷贝,传引用返回有可能会带来数组越界访问,越界访问取到的值是不确定的,因为栈祯销毁了,清不清理是不知道的!
下面继续改动程序验证引用返回的危害:
#include<iostream>
using namespace std;
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = Add(1, 2);
Add(3, 4);
cout << "Add(1,2) is:" << ret << endl;
return 0;
}
用内存空间就像租房子一样,操作系统就是房东,我们申请内存,就是房东把房子给我们用,法律保护别人不会到你的房子里,释放内存就像是,我们退租了。房子还在,但是使用权不是我们的了,房东可能继续租房给别人了。
下面运行这段代码:
#include<iostream>
using namespace std;
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = Add(1, 2);
//Add(3, 4);
printf("hello\\n");
cout << "Add(1,2) is:" << ret << endl;
return 0;
}
输出结果:
那什么情况下可以用引用返回呢?
出了func函数的作用域,ret变量会销毁,就不能用引用返回
出了func函数的作用域,ret变量不会销毁,就可以用引用返回
总结:
A.引用返回的价值是减小拷贝
B.方便实现类似operator[]
以上是关于C++初阶C++入门的主要内容,如果未能解决你的问题,请参考以下文章
C++初阶:入门总结命名空间 | 缺省参数 | 函数重载 | 引用 | 内联函数