《C++》二、C++基本语言

本文介绍了C++中的命名空间用于防止名字冲突,以及如何定义和使用。讨论了引用作为变量别名的特点,常量的const关键字以及constexpr的编译时求值。动态内存管理部分涵盖了new、delete、malloc和free的使用,同时提到了栈和堆的区别。此外,还讲解了vector容器的基本操作及其在范围for循环中的行为。最后,简述了迭代器的概念和不同类型的迭代器,包括常量迭代器。

命名空间

作用:防止名字冲突。命名空间不同,即使函数名称相同也不影响;
特点:定义可以不连续,可以定义在任一位置,写在不同的源文件中;
定义:	
	namespace 命名空间名
	{
		void redius(){...}
	}
示例:
	namespace NMZhangSan
	{
		void redius(){...}
	}
使用方法:
	1NMZhangSan::redius();
	2using namespace NMZhangSan;
	   redius();

输入输出

std::cout  标准输出,向屏幕输出内容;
std::endl  函数模板名,相当于函数指针;
			作用:1、末尾输出换行符\n;
				 2、刷新输出缓冲区,调用flush强制输出缓冲区中数据到屏幕 && 清除缓冲区中数据;
std::cin  标准输入

auto && 头文件防卫 && 引用和常量

C++新标准----可以使用‘{}’在定义变量的时候初始化

int abc =5;  => int abc {5};
int a[] ={11,22,33}; => int a[]{11,22,33};
int abc =3.5f => int abc{3.5} ; //这行编译错误,好处是不会使数据被截断,保证代码健壮性

auto 关键字

auto 可以在声明变量时根据变量初始值的类型自动为此变量选择匹配的类型;
auto的自动推断发生在编译期,所以不会造成程序运行时效率的降低;

auto bvalue =true;
auto ch ='a';

头文件防卫式声明

作用:避免重复#include的问题

/*********头文件定义**************/
"head1.h"
int global_1 =8;

"head2.h"
#include "head1.h"
int global_2 =8;

main.c
#include "head1.h"
#include "head2.h"  
此时编译报错,出现重定义错误 =>这两行展开后是:
	int global_1 =8;
	int global_1 =8;	//重复定义
	int global_2 =8;
/*********头文件定义**************/
"head1.h"
#ifndef _HEAD_
#define _HEAD_
int global_1 =8;
#endif

"head2.h"
#ifndef _HEAD2_
#define _HEAD2_
#include "head1.h"
int global_2 =8;
#endif

main.c
#include "head1.h"
#include "head2.h"  
编译成功

引用

引用是为变量起的别名,简单理解为不额外占用内存

//正确示例
void func(int &ta,int &tb)
{
	ta =2//直接影响main中的a和b
	tb =3;
}
void main()
{
	int a =12;
	int b =33;
	func(a,b);
	cout<< a << " " << b<<endl; //输出 2 3
}
//错误示例
int& ref ;	//错误、定义引用类型必须初始化
int& ref =10;	//错误、引用必须绑定到变量或对象上,不能绑定到常量
int ref =10;
float &ref1 =ref;	//错误、引用类型必须相同

常量

const关键字
const int a =17;
a =18;	//编译报错、不允许修改const修饰的变量值
const int v2 = v1 +max(2,3);	//运行时求值
constexpr C++关键字

作用:编译时求值、能提升运行时的性能

constexpr  int max(int a,int b)
{
	//printf("constexpr自身限制,函数中使用prtintf函数会导致编译失败。打印这句话会导致编译失败"); body of constexpr function is not a return-statement;
	return a +b;
}
constexpr int a =11 +max(2,3);	//要求调用的函数也必须是constexpr 修饰的

范围for && new内存动态分配与nullptr

范围for

int v[]{11,22,33,44};
//for(auto x:v)	存在多余复制,将v的元素值依次复制到x中输出
for(auto &x:v)	//使用引用的方式,避免数据的复制动作
{
	cout<<x<<endl;
	cout<<"x引用的地址" << &x <<" "  <<endl;	//x的地址和v中元素地址相同
}

动态内存分配

内存的5个区域

函数内的局部变量在此创建,由编译器自动分配和释放
由程序员使用malloc/new申请,free/delete释放
全局/静态存储区全局变量和静态变量放这里,程序结束时释放
常量存储区存放常量,不允许修改;如用双引号包含的字符串
程序代码区类似于C语言 的程序代码区

栈和堆的区别

空间有限,分配速度快,系统控制它的分配和释放
空间较大,分配速度较慢,程序员决定和释放,较灵活
malloc和free

malloc和free是系统提供的函数,用于从堆中分配和释放内存。

char* p = (char *)malloc(sizeof(char) * 30);
if (p !=  NULL )
{
	strcpy_s(p, 30, "hello  world i Love u china!!");	//strcpy_s 是strcpy的安全版本,能检测所容纳的元素是否越界,越界则停止程序运行;Buffer is too small;
	cout << p << endl;
	free(p);
}
new和delete

new和delet是运算符,不是函数。
new一般格式如下:

  1. 指针变量名 = new 类型标识符
  2. 指针变量名 = new 类型标识符(初始值)
  3. 指针变量名 = new 类型标识符[内存单元个数];
int * myint = new int;
if(myint != NULL)
{
	*myint =8;
	delete myint;
}
/**********************************************************/
int * myint = new int(18);
if( myint != NULL)
{
	cout<<myint<<endl; //输出18
	*myint = 8;
	cout<<myint<<endl; //输出8
	delete myint;
}
/**********************************************************/
int *a = new int[100]	//开辟一个大小为100的整型数组空间
if(a != NULL)
{
	int *p =a;
	*p++ =1;
	delete[] a; //new用了[],delete也必须用[]。否则回收的内存是第一个数组元素而不是整个数组空间。即使delete[100] a写了数字,数字也会被系统忽略掉;
				//使用vs2022测试,delete a也会初始化100个int大小的内存;即初始化为400个dd(16进制);
}
nullptr

C++11新关键字,表示”空指针";
能使用nullptr就用nullptr,不用NULL;

cout << typeid(NULL).name() << endl;	//vs2022输出int,centos输出l;
cout << typeid(nullptr).name() << endl;	//vs2022输出std::nullptr_t,centos输出Dn;

结构体和类

  1. 结构体内部成员变量和成员函数默认访问级别是public,类的默认访问级别是private;
  2. 结构体的继承默认是public,类继承默认是private;

C++新语法–后置返回类型

auto func(int ,int) ->int; //前面放置auto,表示函数返回类型放置到参数列表之后;
inline内联函数
背景:调用一些函数体很小,频繁调用的函数,需要频繁进行压栈出栈即频繁开辟内存,为解决此类系统性能问题;
inline效果:
	1、影响编译器,在编译阶段完成对Inline函数的处理,即尝试将该函数的动作替换到函数的本体;
	2、编译器可能去做,不同编译器内联结果也不同。无法人为控制;
	3、一般函数声明放在.h文件,inlune函数定义放在.h文件;
const char *、char const * 、char * const的区别
const char * p、char const * p

p指向的内容不能通过p来修改,又称常量指针;

 const char * p;
 *p ='Y';	//编译报错
char * const p

p不可以再指向其他内容,又称指针常量;

char a =1;
char *const p;
p= &a;	//编译报错;
函数形参中带const

功能:不能改变形参中的值
作用:防止无意间修改形参值导致实参值被修改;实参类型更灵活;

void fs(const student &stu)
{
	stu.num =100;	//编译报错,不能修改stu中的值
}
void fs(student &stu){};	//接收普通引用;
void fs1(const student &stu){};	//可以接收普通引用和常量引用;

main(){
	student stu;
	const student &df;
	fs(stu);	//success
	fs(df);		//编译fail,const & 和&类型不匹配
	fs1(stu);	//success;
	fs1(df); 	//success;
********************以下将student类型替换为Int***************************************************
	fs(11);	//fail,必须传递一个变量
	fs1(11);	//success
}

String

引入头文件#include<string>

s.c_str()
功能:返回一个字符串s中内容指针,即一个指向C字符串的常量指针,以’\0’结尾;

string a ="abcd";
//char *p =a.c_str();	编译fail,const char *类型不能初始化char *实体
const char *p =a.c_str();
char str[100];
strcpy_s(str, sizeof(str), p);
cout<<a;	
cout << str;  

字面值和string相加

 string s1="abc";	/success
 string s2= "abc" +'def";	//编译报错,表达式必须具有整数或未区分范围的枚举类型
 string s3 = "abc"+s1+"def";	//success

vector类型

vector类型是标准库中的类型,代表一个容器、集合或动态数组的概念。可以把一组相同类型的类对象放到vector中;
引入头文件
vector存放对象,引用只是一个别名不是一个对象;

#include<vector>
int main()
{
	vector<int> v;		//success
	vector<int *> b;	//success
	b.push_back(v);
	vector<int &> c;	//运行fail,无法创建指向引用的指针;
}

初始化

vector<int> a(10);	//圆括号括住10,表示元素数量,每个元素默认值0
vector<int> a(10,1);	//圆括号括住10,表示元素数量,每个元素值为1;
vector<int> a{10};	//中括号括住10,表示一个元素10;
vector<int> a{101};	//两个元素10和1;
vector<string> a{10};	//10个元素,每个元素"";
vector<string>{10,"string"};//10个元素,每个元素都是"string";

***范围for的应用中,如果增加vector容量的代码,则输出会变得混乱;
依据以下调试内容猜原因应该是每次对vector容器增减对象后,vector容器会重新分配一块内存存储容器对象;
然而原vec记录的地址是vector变化前的地址,vec记录的容器大小是最初容器的大小;


vector<int>a{ 1,2,3};
for (auto vec : a)
{
	cout << vec << endl;
}
输出:
1
2
3
/***********************************************************************/
vector<int>a{ 1,2,3,4};
for (auto vec : a)
{
	a.push_back(66);
	cout << vec << endl;
}	//调试每次执行到这一行,&vec显示未定义,说明每次循环vec都是重新赋值的;
输出:
1
-572662307
-572662307
-572662307
第一次循环:
&a:0x00000034ABDAF8E8,
&a[0]0x0000016827201C10[1],
&a[1]0x0000016827201C14[2]
&a[2]0x0000016827201C18[3],
&a[3]0x0000016827201C1C[4]
&vec:0x00000034ABDAF984[1],
以下无法计算表达式:
&a[4]:
&a[5]:
&a[6]:
&a[7]:

第二次循环:
&a:0x00000034ABDAF8E8,
&a[0]0x00000168271FF5B0[1],
&a[1]0x00000168271FF5B4[2]
&a[2]0x00000168271FF5B8[3],
&a[3]0x00000168271FF5BC[4]&a[4]: 0x00000168271FF5C[66],
&vec:0x00000034ABDAF984[-33686272],
以下无法计算表达式:
&a[5]:
&a[6]:
&a[7]:
.....
第四次循环后就结束了:
&a:0x00000034ABDAF8E8,
&a[0]0x00000168271FF430[1],
&a[1]0x00000168271FF434[2]
&a[2]0x00000168271FF438[3],
&a[3]0x00000168271FF43C[4]&a[4]: 0x00000168271FF440[66],
&a[5]:0x00000168271FF444[66],
&a[6]:0x00000168271FF448[66],
&vec:0x00000034ABDAF984[-33686272],
以下无法计算表达式:
&a[7]:
vector<int>a{ 1,2,3,4};
for (auto& vec : a)
{
	a.push_back(66);
	cout << vec << endl;
}	//调试每次执行到这一行,&vec显示未定义,说明每次循环vec都是重新赋值的;
/*******************************************************************************/
第一次循环前:
&a[0]: 0x0000027767861E10[1]
&vec: 未定义
第一次循环a.push_back(66);执行前:
&a[0]: 0x0000027767861E10[1]
&vec:  0x0000027767861E10[1]
第一次循环a.push_back(66);执行后:
&vec: 0x0000027767861E10[-572662307]
&a[0]:0x000002776785F270[1]

迭代器

简介:迭代器是一种遍历容器内元素的数据类型,也可以理解为是用来指向容器内的某个元素的。

任何能改变vector对象容量的操作如push_back,都会使当前的vector对象迭代器失效。

迭代器begin/end、反向迭代器rbegin/rend

begin返回的迭代器指向容器中第一个元素
end返回的迭代器指向容器最后一个元素后的位置

vector<int> iv = { 100,200,300 };
for (vector<int>::iterator iter = iv.begin(); iter != iv.end(); iter++)
{
	cout << *iter << endl;
	delete (*iter);	//删除迭代器元素所指向的内存,并没有修改容器容量;
}

rbegin返回的迭代器指向容器中最后一个元素
rend返回的迭代器指向容器第一个元素前的位置

vector<int> iv = { 100,200,300 };
for (vector<int>::reverse_iterator iter = iv.rbegin(); iter != iv.rend(); iter++)
{
	cout << *iter << endl;
}

const_iterator迭代器

特点:该迭代器所指向的元素的值不能改变;只能从容器中读元素;

vector<int> iv = { 100,200,300 };
for (vector<int>::const_iterator iter = iv.begin(); iter != iv.end(); iter++)
{
	//*iter = 4;  编译报错,表达式必须是可修改的左值;
	cout << *iter << endl;
}

cbegin和cend返回的也是常量迭代器const_iterator

vector<int> iv = { 100,200,300 };
for (auto iter = iv.cbegin(); iter != iv.cend(); iter++)
{
	//*iter = 4;  编译报错,const_iterator类型,表达式必须是可修改的左值;
	cout << *iter << endl;
}

类型转换:static_cast,reintrpret_cast等

强制类型转换

  1. static_cast:静态转换 转换阶段:编译阶段;
a.相关类型转换
	double f = 1.13f;
	int i = (int)f;	//c风格
	int i2 = static_cast <int> f;	//c++风格
/**********************************************************************************************/
b.子类转父类
	A a = static_const<A>(b);
/**********************************************************************************************/
c.void* 与其他类型指针之间的转换
	int i = 10;
		nt* p = &i;
	void* q = static_cast<void*>(p);	
	int* db = static_cast<int*>(q);
	cout <<  *db << endl;
	//cout<<*q<<endl;	无法输出*q,表达式必须是指向完整类型对象的指针
/**********************************************************************************************/
d.不能用于指针类型之间的转换
double f =10.12;
double *pf =&f;
int *i = static_cast<int *>(pf);	//编译失败,类型转换无效
  1. dynamic_cast:运行时类型识别和检查,保证安全&&代价昂贵
  2. const_cast:去除指针或引用的const属性 编译阶段
  3. reinterpret_cast:处理无关类型的转换,编译阶段
int i =10;
char *pc = reinterpret_cast<char *)(&i);	//两个不同类型随便转换
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值