C++基础精讲-02

文章目录

  • 1.C/C++申请、释放堆空间的方式对比
  • 2.引用
    • [2.1 引用的概念](#2.1 引用的概念)
    • [2.2 引用的本质](#2.2 引用的本质)
    • [2.3 引用与指针的联系与区别](#2.3 引用与指针的联系与区别)
    • [2.4 引用的使用场景](#2.4 引用的使用场景)
      • [2.4.1 引用作为函数的参数](#2.4.1 引用作为函数的参数)
    • [2.5 引用作为函数的返回值](#2.5 引用作为函数的返回值)
    • [2.6 总结](#2.6 总结)
  • 3.强制类型转换
    • [3.1 c语言里的强制类型转换](#3.1 c语言里的强制类型转换)
    • [3.2 c++数据类型强制转换](#3.2 c++数据类型强制转换)
      • [3.2.1 static_cast(常用)](#3.2.1 static_cast(常用))
      • [3.2.3 const_cast (常不用)](#3.2.3 const_cast (常不用))
      • [3.2.3 其他(后面章节中应用到在讲解)](#3.2.3 其他(后面章节中应用到在讲解))

1.C/C++申请、释放堆空间的方式对比

1.1C语言申请、释放堆空间

关于C语言的堆内存管理内存管理不熟悉可看以下章节
C语言精讲-12
c语言中申请、释放堆空间一般用malloc和free函数

c 复制代码
#include<stdio.h>
#include<stdlib.h>

void test(void)
{
        //申请内存
        int*p=(int*) malloc(sizeof(int));
        if(NULL==p)
        {
                printf("内存申请失败\n");
                exit(0) ;
        }
        *p=10;
        printf("*p=%d\n",*p);

        //释放内存;
        free(p);
        p=NULL;

}

int main(void)
{
        test();
        return 0;

}

1.2C++申请、释放堆空间

c++申请、释放堆空间一般用new/delete表达式;

cpp 复制代码
#include<iostream>
using std::cout;
using std::endl;

void test(void)
{
        //初始化为该类型的默认值
        int * p1 = new int();
        cout<<*p1<<endl;

        int *p2=new int(2);
        cout<<*p2<<endl;

        //释放内存
        delete p1;
        delete p2;
        //将 p1 和 p2 置为 nullptr;避免野指针
        p1= nullptr;
        p2= nullptr;

}

int main(void)
{
        test();
        return 0;

}

1.2.1 new表达式申请数组空间

1.默认初始化为0;

cpp 复制代码
#include<iostream>
using std::cout;
using std::cin;
using std::endl;

void test(void)
{
        //将动态分配的数组元素默认初始化为 0
        //申请10个int类型的堆空间
        int * p1 = new int[10]();
        for(int idx = 0; idx < 10; ++idx)
        {
        p1[idx] = idx;
        }

        for(int idx = 0; idx < 10; ++idx)
        {
        cout << p1[idx] << endl;
        }
        //释放堆空间
        delete [] p1;
        p1=nullptr;


}

int main(void)
{       
        test();
        return 0;
}       
~                                       


2.赋初值

cpp 复制代码
#include<iostream>
using std::cout;
using std::cin;
using std::endl;

void test(void)
{       
        //申请3个int类型的堆空间
        int * p1 = new int[3]{2,4,6};
        for(int i=0;i<3;i++)
        {
                cout<<p1[i]<<endl;
        }
        //释放堆空间
        delete [] p1;
        p1=nullptr;


}

int main(void)
{       
        test();
        return 0;
}       
~                           

1.3回收空间时的注意事项

1.三组申请空间和回收空间的匹配组合

复制代码
malloc            free
    
new               delete
    
new int[5]()      delete[]

2安全回收

delete只是回收了指针指向的空间,但这个指针变量依然还在,指向了不确定的内容(野指针),容易造成错误。所以需要进行安全回收,将这个指针设为空指针C++11之后使用nullptr表示空指针。

1.4malloc/free 和 new/delete 的区别

1.malloc/free 是库函数;new/delete 是表达式,后两者使用时不是函数的写法;

2.new 表达式的返回值是相应类型的指针,malloc 返回值是 void*;

3.malloc 申请的空间不会进行初始化,获取到的空间是有脏数据的,但 new 表达式申请空间时可以直接初始化;

4.malloc 的参数是字节数,new 表达式不需要传递字节数,会根据相应类型自动获取空间大小。

2.引用

2.1 引用的概念

引用是c++对c的重要扩充 。在c/c++中指针的作用基本都是一样的,但是c++增加了另外一种给函数传递地址的途径,这就是按引用传递
变量概述

1.变量名实质上是一段连续内存空间的别名,是一个标号(门牌号)

2.程序中通过变量来申请并命名内存空间

3.通过变量的名字可以使用存储空间
c++中新增了引用的概念,引用可以作为一个已定义变量的别名。
基本语法:

复制代码
Type& ref = val;
示例:
int number = 2;
int & ref = number;
cpp 复制代码
#include<iostream>
using std::cout;
using std::cin;
using std::endl;


int main(void)
{

        int sum=10;
        cout<<"sum="<<sum<<endl;
        int&s=sum;
        cout<<"s="<<s<<endl;
        s=100;
        cout<<"s="<<s<<endl<<"num="<<sum<<endl;

        return 0;
}
                                            


注意事项

&在此不是求地址运算,而是起标识 作用。

类型标识符是指目标变量的类型

必须在声明引用变量时进行初始化。

引用初始化之后不能改变

不能有NULL引用。必须确保引用是和一块合法的存储单元关联。

2.2 引用的本质

引用的本质在c++内部实现是一个常指针;c++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见。

c 复制代码
#include<iostream>
using std::cout;
using std::cin;
using std::endl;

void test(void)
{
        int sum=10;
        int &s=sum;
        int*p=&sum;
        cout<<"sum的地址:"<<&sum<<endl;
        cout<<"s的地址:"<<&s<<endl;
        cout<<"p所指向的地址:"<<p<<endl;

        cout<<"size(p)="<<sizeof(p)<<endl;//指针p占多少字节
        cout<<"size(s)="<<sizeof(&s)<<endl;//引用&s占多数字节
}

int main(void)
{

        test();


        return 0;
}

                        

2.3 引用与指针的联系与区别

联系:

  1. 引用和指针都有地址的概念,都是用来间接访问变量;

  2. 引用的底层还是指针来完成,可以把引用视为一个常指针。

区别

  1. 引用必须初始化,指针可以不初始化;

  2. 引用不能修改绑定,但是指针可以修改指向;

  3. 在代码层面对引用本身取址取到的是变量本体的地址,但是对指针取址取到的是指针变量的地址

2.4 引用的使用场景

2.4.1 引用作为函数的参数

在没有引用之前,如果我们想通过形参改变实参的值,只有使用指针才能到达目的。但使用指针的过程中,不好操作,很容易犯错。 而引用既然可以作为其他变量的别人而存在,那在很多场合下就可以用引用代替指针,因而也具有更好的可读性和实用性。这就是引用存在的意义。

c 复制代码
#include<iostream>
using std::cout;
using std::cin;
using std::endl;

//使用指针
void swap(int *a,int*b)
{
        int temp=*a;
        *a=*b;
        *b=temp;
}
//使用引用
void swap2(int&a,int&b)
{
        int temp=a;
        a=b;
        b=temp;
}

void test1(void)
{
        int a=10,b=20;
        cout<<"交换前:a="<<a<<",b="<<b<<endl;
        swap(&a,&b);
        cout<<"交换后:a="<<a<<",b="<<b<<endl;
        swap2(a,b);
        cout<<"再次交换后:a="<<a<<"b="<<b<<endl;
}
int main(void)
{
        test1();
        return 0;
}

2.5 引用作为函数的返回值

当以引用作为函数的返回值时,返回的变量其生命周期一定是要大于函数的生命周期 的,即当函数执行完毕时,返回的变量还存在。

目的: 避免复制,节省开销

cpp 复制代码
#include<iostream>
using std::cout;
using std::cin;
using std::endl;

int fun1(void)
{
        int a=10;

        //返回的是a的副本,变量a是局部变量。在fun1函数结束时a的空间就被回收
        cout<<"fun a的地址:"<<&a<<endl;
        return a;
}

int& fun2(void)
{
        ///使用指针/引用作为返回值时候,不可返回一个局部的变量;可加static修饰
        static int  b=100;

        //返回的是一个绑定b的的引用;
        cout<<"fun2 b的地址:"<<&b<<endl;
        return b;
}

int main(void)
{
        //把fun1中a的值复制给main函数中的变量a;
        int a=fun1();
        cout<<"main中a的地址:"<<&a<<endl;

        int &b=fun2();
        cout<<"main 中引用b的地址:"<<&b<<endl;

        return 0;
}


注意事项

1.不要返回局部变量的引用 。因为局部变量会在函数返回后被销毁,被返回的引用就成为了"无所指"的引用,程序会进入未知状态。

2.不要轻易返回一个堆空间变量的引用,非常容易造成内存泄漏。

cpp 复制代码
int & func()
{
	int * pint = new int(1);
	return *pint;
}

void test()
{
	int a = 2, b = 4;
	int c = a + func() + b;//内存泄漏
}

2.6 总结

  1. 在引用的使用中,单纯给某个变量取个别名没有什么意义,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不理想的问题。
  2. 用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,还可以通过const的使用,保证了引用传递的安全性。
  3. 引用与指针的区别是,指针通过某个指针变量指向一个变量后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;引用底层仍然是指针,但是编译器不允许访问到这个底层的指针,逻辑上简单理解为------对引用的操作就是对目标变量的操作。可以用指针或引用解决的问题,更推荐使用引用

3.强制类型转换

3.1 c语言里的强制类型转换

格式:数据类型 变量1 = (数据类型)变量2;

c 复制代码
#include<stdio.h>

int main(void)
{       
        float a=3.14f;

        printf("a=%f\n",a);
        int b=(int)a;
        printf("b=%d\n",b);
        
        return 0;
}       

缺点:

  1. 数据丢失风险高
    C 语言的强制类型转换语法简单,不过在将大类型转换为小类型时,容易发生数据丢失。比如把long类型转换为int类型,若long类型的值超出int类型的范围,就会出现数据截断。
  2. 破坏类型系统且缺乏安全性检查
    C 语言允许进行各种类型的强制转换,像指针类型的转换,这可能会破坏类型系统的安全性,编译器也不会做严格的类型检查。例如将void*指针转换为其他类型的指针,若使用不当,就会引发未定义行为。
  3. 掩盖潜在错误
    C 语言的强制类型转换可能会掩盖代码中的潜在错误,使代码能通过编译,但实际上逻辑可能存在问题。
  4. 可移植性差
    不同平台上数据类型的大小和表示方式可能不同,C 语言的强制类型转换可能会导致在不同平台上产生不同的结果,影响代码的可移植性。

3.2 c++数据类型强制转换

3.2.1 static_cast(常用)

最常用的类型转换符,在正常状况下的类型转换, 用于将一种数据类型转换成另一种数据类型,如把int转换为float

c++ 复制代码
目标类型 转换后的变量 = static_cast<目标类型>(要转换的变量)

好处:不允许非法的转换发生;方便查找

cpp 复制代码
int a = 100;
float f = 0;
f = (float) a;//C风格
f = static_cast<float>(a);
cpp 复制代码
void * p1 = malloc(sizeof(int));
int * p2 = static_cast<int*>(p1);
*p2 = 1;

不能完成任意两个指针类型间的转换(限制一些非法转换)

cpp 复制代码
int a = 1;
int * p1 = &a;
float * p2 = static_cast<float *>(p1);//错误

总结,static_cast的用法主要有以下几种:

复制代码
1)用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性需要开发人员来保证;
2)把void指针转换成目标类型的指针,但不安全;
3)把任何类型的表达式转换成void类型;
4)用于类层次结构中基类和子类之间指针或引用的转换

3.2.3 const_cast (常不用)

该运算符用来修改类型的const属性。

指向常量的指针被转化成普通指针,并且仍然指向原来的对象;

常量引用被转换成非常量引用,并且仍然指向原来的对象;

cpp 复制代码
const int number = 100;
int * pInt = &number;//error
int * pInt2 = const_cast<int *>(&number);

3.2.3 其他(后面章节中应用到在讲解)

dynamic_cast:该运算符主要用于基类和派生类间的转换;

reinterpret_cast:功能强大,慎用(也称为万能转换);

相关推荐
星星火柴93622 分钟前
数据结构:哈希表 | C++中的set与map
数据结构·c++·笔记·算法·链表·哈希算法·散列表
搬砖工程师Cola1 小时前
<C#>在 C# .NET 6 中,使用IWebHostEnvironment获取Web应用程序的运行信息。
开发语言·c#·.net
没有啥的昵称2 小时前
从源码安装ROS的serial包(替换github的方案)
c++
八了个戒3 小时前
「数据可视化 D3系列」入门第三章:深入理解 Update-Enter-Exit 模式
开发语言·前端·javascript·数据可视化
失去妙妙屋的米奇3 小时前
matplotlib数据展示
开发语言·图像处理·python·计算机视觉·matplotlib
夏天的阳光吖3 小时前
C++蓝桥杯实训篇(四)
开发语言·c++·蓝桥杯
angushine4 小时前
Gateway获取下游最终响应码
java·开发语言·gateway
小乐xiaole4 小时前
蓝桥杯 2025 C++组 省 B 题解
c++·蓝桥杯·深度优先
西贝爱学习5 小时前
数据结构:C语言版严蔚敏和解析介绍,附pdf
c语言·开发语言·数据结构
程丞Q香5 小时前
python——学生管理系统
开发语言·python·pycharm