C++第三篇 (引用 内存管理 类型转换)

  • C++ 第三篇
  • 一、内存管理
  • 二、引用

一 、内存管理

二 、引用

一、内存管理

内存管理

  1. 分配 : new, new[]
  2. 释放: delete, delete[]
#include <iostream>
using namespace std;
int main(void)
{
	// int *pi =(int*)malloc(sizeof(int)); //  c 语言中的形式
	int *pi = new int;
	*pi = 123;
	cout << *pi <<endl;
	delete pi;
	pi = NULL;
	// init pi2 when new
	int *pi2 = new int(123);
//	*pi2++; // error  pi2先++ ,非法访问后面的内存
	(*pi2)++; //
	cout << *pi2 <<endl;
	delete pi2;  // 如果上面非法访问后面的内存,这里会出现error,相当没有new 那一块内存,但是删除了
	pi2 = NULL;
}

c++ 中数组的分配和释放

#include <iostream>
using namespace std;
int main(void)
{
	int * parry = new int[10];
	for(int i=0; i< 10; i++){
		parry[i] = i;
		cout << parry[i] << endl;
	}

	delete[] parry;
	parry = NULL;
	// c++ 11 
	int * parry2 = new int[10]{1,2,3,4,5,6,7,8,9,10};
	for (int i=0; i< 10; i++)
	{
		cout << parry2[i] << endl;
	}
	delete[] parry2;
	parry2 = NULL;
}

指针使用的错误 实例

#include <iostream>
using namespace std;
int main()
{
	int * pi;
	delete pi;// Delete wild pointer dangerous!  释放野指针  很危险,可能出错

	int *pi2 = NULL;
	delete pi2; // 可以释放空指针,但是没有任何的意义 OK but no means
	int *pi3 = new int;
	delete pi3;
	delete pi3; //  连续两次释放一块内存,会出错
}

二 C ++ 的引用

2.1 定义

引用即别名,就是某个变量的别名,对引用的操作和对变本身的操作完全相同
注意: 引用在定义式必须初始化,初始化以后绑定其他变量名,不能再修改。
注意: 引用类型和绑定的变量类型必须要一致

2.2 语法:

类型 & 引用名 = 变量;
int a = 100;
int & b = a; // b 是a 的别名
b++;
cout << a<< endl; // 相当于执行a++
char & r = a; // 错误 类型不一致

2.3 常引用

2.3.1

定义引用时加上const修饰,即为常引用,不能通过引用修改引用的目标变量。
const 类型 & 引用名 = 变量;
类型 const & 引用名 = 变量; // 和上面完全相同
int a = 100;
int &b = a;
a++; // ok
b++; // error

2.3.2

普通引用也叫做左值引用,只能引用左值。而常引用也叫做万能引用,既可以引用左值,也可以引用右值;

using namespace std;
int func(void)
{
	int num = 100;
	cout << "&num = "<< &num<<endl;
	return num; // 
}

int main()
{
	int a = 123;
	int &b = a; //  
	int &c = 100; //  error 
	const int &d = 200;
	//  1. 首先将a 转化为char,结果保存在临时变量中
	2. e实际引用的是临时变量,临时变量是右值
	3.  普通引用只能引用左值,因此出现错误
	char& e = a; // error
	const char &f =a; // ok  //  使用万能引用可以引用右值,因此正确
	int i = func(); //  函数调用,返回的结果也是一个临时变量,临时变量是右值
	const int &r3 = func();  //  因此引用需要加上const 
	cout << "&r3 =" << &r3<< endl; //  可以看到程序运行后,r3 的地址和num 的地址不一样

	return 0;	
}



2.3.3

关于左值和右值
左值(lvaule) : 能放在赋值表达式左侧,可以被修改
右值(rvalue) : 只能放在赋值表达式右侧,不能被修改

// 一下这些是左值
int  a =100
int b = 200; 
a+= b = 300   //  
++a // 左值  相当于 a = a+1;
// 以下这些是右值
(a + b)  //  因为a+ b 的结果放在了临时变量中
a ++ // 右值  
cout << a++ <<endl; // 这个是100
cout << a << endl; // 这个是101
a ++ 先算的是++ ,但是该表达式表示 先将a 的值存入临时变量,然后++ 操作赋值给a. 因此 a++ 返回的其实是a 的临时变量的值, a本身完成了++ 操作

2.4 引用型函数参数

  1. 可以将引用用于函数参数,这是实参就是形参的别名,可以通过形参直接修改实参的值,同时还可以避免参数传值的开销,提高效率。
  2. 引用型的参数有可能意外的修改实参的值,如果不希望修改实参本身,可以将形参定义为常引用,提高传参效率的同时,还可以接收常量实参。
#include <iostream>
using namespace std;

void swap1 (int &x, int &y)
{
	x = x^y;
	y= x^y;
	x = x^y;
}
void swap (int *x, int *y)
{
	*x = *x^*y;
	*y= *x^*y;
	*x = *x^*y;
}
int main(void)
{
	int a = 10;
	int b = 20;
	swap(&a,&b);
	cout << "a ="<< a<<"  "<< "b=" << b<< endl;
	swap1(a,b);
	cout << "a ="<< a<<"  "<< "b=" << b<< endl;

}

结构体传参数引用

#include <iostream>
using namespace std;

struct Teacher {
	char name[100];
	int age;
	double salary;
};

void print(const struct Teacher& teacher)
{
	cout << teacher.name<< teacher.age/*++*/<< teacher.salary<<endl;
}
int main()
{
		const struct Teacher t1 = {"xiaoming",32,8000.3}; //  如果变量是常类型变量,那么引用必须是常引用
		print(t1) ;
		return 0;
}

2.5 引用型函数返回值

  1. 可以将函数的返回值声明为引用,这时函数的返回值结果就是return 后面的数据的别名,可以避免返回值所带来的开销,提高代码的传参效率。
  2. 如果返回类型是左值引用,那么该函数调用表达式的结果也是一个左值
  3. 不能返回局部变量的引用,因为所引用的内存会在函数释返回后释放,使用危险!!! 但是可以从函数中返回成员变量,静态变量,全局变量的引用
#include <iostream>
using namespace std;

struct Teacher {
	int age;
	int& get_age()
	{
		int num = 100;
		return num; // 这是很危险的,返回的局部变量会被销毁,则引用相当于不知道引用了什么。。。,结果不可预知
		//return age;
	}
};

int main()
{
		struct Teacher t1 = {20};
		t1.get_age() = 30;
		cout << t1.age<< endl;
		return 0;

}

2.6 引用和指针

  1. 从C语言的角度看,引用的底层实现是指针。但是C++推荐使用引用,不推荐使用指针。
    int i = 100;
    int & ri= i;
    int * const pi = i;
    ri 与 *pi 是等价的
    底层的汇编代码是等价的。
  2. 指针可以不做初始化,指向的目标变量可以在初始化以后改变。
    引用必须初始化,而且一旦初始化,其目标将不会改变。
    int a = 10, b = 20;
    int *p ;
    p = &a;
    p = &b;
    int & r // error
    int & r = a;
    r = b ; // 仅仅是一个赋值操作

3.1 可以定义指针的指针(二级指针),但是没有引用的指针
3.2 可以定义指针的引用(指针的别名),但是没有引用的引用
3.3 可以定义指针数组,但是不能定义引用数组
但是可以定义数组引用(数组的别名)
3.4 可以定义指针数组。但是不可以定义引用数组。但是也可以定义数组引用(数组别名)
3.5 可以定义函数的指针,也可以定义函数的引用,语法特定一致

#include <iostream>
using namespace std;

int func(int i, int j)
{
	return (i+j);
}

int main()
{
	int a = 100;
	int *p = &a;
	int **pp = &p;
	
	int &r = a;

	int & * rp = r; // error  没有引用的指针,因为引用没有 地址,定义引用的指针也就没有意义

	int * & pr = p;  //  可以有指针的引用
 
	int && rr = r; // error  没有引用的引用

	int i= 10, j= 20, k = 30;
	int *parr[3] = {&i,&j,&k}; // ok

//	int & rarr[3] ={i,k,k}; // error  没有引用数组
	

	int arr[3] ={i,j,k};
	int (&rarr)[3] = arr; // ok 可以有数组的引用,相当于数组的别名
	
	int (*pfunc)(int, int) = func;
	pfunc(i,j);

	int (&rfunc)(int, int) =  func;
	rfunc(j,k);

	return 0;
}

三 类型转换

  1. 隐式类型转换
char c = 'a';
int i = c;
-------------------------------------
void func(int i) {...}
func (c)
--------------------------------------
int func (void)
{
	char c = 'a';
	return c;
}
  1. 显式类型转换
    c++ 兼容C 中的隐式类型转换
    2.1 强制转换
    char c = ‘a’;
    int i = (int) c; // c风格
    int i = int©; // c++ 风格
    2.2 c++ 拓展了四种操作符形式的类型转换
    2.2.1.
    1. 静态类型转换 static_cast
      语法: 目标类型变量= static_cast<目标类型>(源类型变量)
      适用场景: 将void * 的指针转换为其他类型的指针。
      使用static_cast 更加安全,如果将整形指针转化为整形,就会失败。
#include <iostream>
using namespace std;
int main()
{
	int *pi =NULL;
	//  static cast 
	void * pv = pi;
	pi =static_cast<int *> (pv);
	int i = static_cast<int>(pi); // 此处不合理,因此编译器报错  
	return 0;
}
2. 动态类型转换dynamic_cast
语法: 目标类型变量= dynamic_cast<目标类型>(源类型变量) 主要用于多态,父子类型转换
3. (去)常类型转换 : const_cast
语法: 目标类型变量= const_cast<目标类型>(源类型变量) 主要用于去除指针或者变量的常属性
#include <iostream>

using namespace std;
int main()
{
	const int ci = 100;
	int *pi = &ci; // error 在c++ 中,使用直接将const int 转换为int 是错误的,相当于扩大了范围
	int* pi = const_cast<int*> (&ci); // 需要去常转换  
	return 0;
}

#include <iostream>
using namespace std;
int main()
{
	const int ci = 100;
	int* pi = const_cast<int*> (&ci); //  error  
	*pi = 200;
	cout << "ci = "<< ci << endl;
	cout<< "*pi = "<< *pi << endl;
	return 0;
}

这段代码的输出结果是
ci = 100
*pi = 200
4. 重解释类型转换: reinterpret_cast

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值