c++八股文之----c++11新特性

右值引用

左值

1)左值可以取地址
2)左值可以修改
3)左值可以放在等号左右两边

右值

1)右值不可以取地址
2)右值不可以直接修改
3)右值只能放在等号右边
4)右值往往是没有名称的

右值分为纯右值和将亡值

纯右值

int a = 3; 就是指等号右边的常数,上式中的3

将亡值

将亡值其实就是中间变量的过渡,过渡之后就消亡

函数的临时返回值 int a = f(3)

表达式 像(x+y)

在左右值使用时有三个原则,不能违反原则,否则编译无法通过:

原则1:右值可以赋给左值,左值不能给右值(左值权限更大)

原则2:右值无法修改

原则3:编译器允许为左值建立引用,不可以为右值建立引用

右值引用

右值引用的语法:&&

使用右值引用需要注意三个问题:

1)右值引用必须要进行初始化

int && a;  × // 必须初始化

2)不能使用左值进行初始化

int num = 10;
int && a = num; × //不能使用左值进行右值初始化
int && a = 10; √ 

3)右值引用可以对右值进行修改

int &&a = 10;    // 这里的a是右值引用,其实是10
a = 100;

可以发现当对右值加上应用后可以修改值也可以修改地址,从功能上升为左值。所以有一种说法:右值引用的本质就是不用拷贝的左值。

通用引用

这种引用在源代码中(“T&&”)看起来像右值引用,但是它们可以变现左值引用(“即 T&”)的行为。它们的双重性质允许它们绑定右值和左值。而且,它们可以绑定const或非const对象,可以绑定volatile和非volatile对象,还可以绑定const和volatile同时作用的对象。它们实际上可以绑定任何东西。构成通用引用有两个条件:

1.必须满足T&&这种形式(即使加上const也不行)

2.类型T必须是通过推断得到的(最常见的就是模板函数参数)

比如将一个左值传递给函数的右值引用参数,且此右值引用指向模板类型参数时,编译器推断,模板类型参数为:实参的左值引用

template<typename T> void f(T&&);
int i = 1;
f(i);//编译器推导出T为int&类型
f(10);//推导出T为普通类型int

引用折叠

通常不能直接定义引用的引用(引用非实体),但是通过类型别名或模板类型参数间接定义是可以的,但这时引用会形成“折叠”,例如上例中,当T被推导为int&,f的参数类型变为int& &&(代码无效,用于演示),引用折叠规则:

1.X& &,X& &&,X&& &折叠为:X&

2.X&& &&折叠为:X&&

于是最后我们得到的f的形参类型是int&类型,即当我们用左值对f进行调用时,实际使用传引用的方式在调用函数。

std::move

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
	return static_cast<typename remove_reference<T>::type &&>(t);
}

首先,通过右值引用传递模板实现,利用引用折叠原理将右值经过T&&传递类型保持不变还是右值,而左值经过T&&变为普通的左值引用,以保证模板可以传递任意实参,且保持类型不变。(先能够把参数类型全都接收)

然后我们通过static_cast<>进行强制类型转换返回T&&右值引用,而static_cast之所以能使用类型转换,是通过remove_refrence::type模板移除T&&,T&的引用,获取具体类型T(模板偏特化)。(再把接收的参数的原引用抹除强转成右引)

它唯一的功能是将一个左值引用强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。

移动语义

在c++中,“move”语义是通过将资源的所有权从一个对象转移到另一个对象,以减少不必要的复制和内存分配。它通过使用特殊的移动构造函数和移动赋值运算符来实现。

通俗的说,当我们需要将一个对象的值赋给另一个对象时,通常会发生复制操作,其中包括拷贝构造函数和拷贝复制运算符。之涉及到将源对象的数据复制到新对象中,这可能需要分配新的内存并进行数据复制。

然而,有时我们并不需要创建对象并进行数据复制,而只是希望将源对象的资源转移到新对象中,同时将源对象置于有效但未定义的状态。这是“move”语义发挥作用的地方。

#include <iostream>
#include <vector>
 
class MyObject {
public:
    MyObject() {
        std::cout << "Default Constructor" << std::endl;
    }
 
    MyObject(const MyObject& other) {
        std::cout << "Copy Constructor" << std::endl;
    }
 
    MyObject(MyObject&& other) noexcept {
        std::cout << "Move Constructor" << std::endl;
    }
 
    MyObject& operator=(const MyObject& other) {
        std::cout << "Copy Assignment Operator" << std::endl;
        return *this;
    }
 
    MyObject& operator=(MyObject&& other) noexcept {
        std::cout << "Move Assignment Operator" << std::endl;
        return *this;
    }
};
 
int main() {
    MyObject obj1;
    MyObject obj2(std::move(obj1));  // 移动构造函数
 
    MyObject obj3;
    obj3 = std::move(obj2);  // 移动赋值运算符
 
    std::vector<MyObject> vec;
    vec.push_back(std::move(obj3));  // 向容器中移动对象
 
    return 0;
}

完美转发

一个右值引用作为函数参数的型参时,在函数内部转发该参数给内部其他函数时,它就变成一个左值(已实名的右值编译器当作左值处理),并不是原来的类型了。如果需要按参数原来的类型转发到另一个函数,可以使用c++11提供的std::forward() 函数,该函数实现的功能称之为完美转发。

#include<iostream>
using namespace std;
template<typename T>
void PrintT(T& t)
{
    cout << "lvalue" << endl;
}
template<typename T>
void PrintT(T&& t)
{
    cout << "rvalue" << endl;
}
template<typename T>
void TestForward(T&& v)
{
    PrintT(v);
    PrintT(move(v));
    PrintT(forward<T>(v));
    cout << endl;
}
int main()
{
    TestForward(1);
    int x = 1;
    TestForward(x);
    TestForward(forward<int>(x));
    TestForward(forward<int&>(x));
    TestForward(forward<int&&>(x));
    return 0;
}

在这里插入图片描述

lambda

Lambda表达式语法定义
在这里插入图片描述

1.捕获列表:捕获列表总是出想在Lambda函数的开始处,编译器根据该引出符判断接下来的代码是否是Lambda函数,捕获列表能够捕捉上下文的变量一共Lambda函数使用,不允许变量重复传递。

2.参数列表:与普通函数的参数列表一致。如果不需要参数传递,则可以连括号“()”一起省略。

3.可变规格:mutable修饰符,默认情况下Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)。

4.异常说明:用throw()抛出异常。

5.返回类型:可以在不需要返回值的时候也可以联通符号“->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器自行推导(只有一行函数体才能自行推到,否则需要写明返回类型)。

6.函数体:内容与普通函数一样,不过除了可以使用参数外,还可以使用所有捕获的变量。

捕获列表

[ ]表示不捕获任何变量

auto function = ([]{
		std::cout << "Hello World!" << std::endl;
	}
);

function();

[var]表示值传递方式捕获变量var

int num = 100;
auto function = ([num]{
		std::cout << num << std::endl;
	}
);

function();

[&var]表示引用传递捕获变量var

int num = 100;
auto function = ([&num]{
		num = 1000;
		std::cout << "num: " << num << std::endl;
	}
);

function();

[=]表示值传递方式捕获所有父作用的变量(包括this)

int index = 1;
int num = 100;
auto function = ([=]{
			std::cout << "index: "<< index << ", " 
                << "num: "<< num << std::endl;
	}
);

function();

[&]表示引用传递方式捕获所有父作用域的变量(包括this)

int index = 1;
int num = 100;
auto function = ([&]{
		num = 1000;
		index = 2;
		std::cout << "index: "<< index << ", " 
            << "num: "<< num << std::endl;
	}
);

function();

[=,&]拷贝与引用混合

[=,&a,&b]表示一引用传递的方式捕获变量a和b,以值传递方式捕获其他所有变量

int index = 1;
int num = 100;
auto function = ([=, &index, &num]{
		num = 1000;
		index = 2;
		std::cout << "index: "<< index << ", " 
            << "num: "<< num << std::endl;
	}
);

function();

[&,a,this]表示以值传递的方式捕捉变量a和this,引用传递方式捕捉其他所有变量

优点

不需要额外写一个函数或函数对象,避免了代码膨胀和功能能分散。在需要的时间和地点实现功能闭包,使程序更灵活

工作原理

编译器会把一个Lambda表达式生成一个匿名类的匿名对象,并在类中重载函数调用运算符,实现了一个operator()方法。

auto print = []{cout << "Hello World!" << endl; };

编译器会把上面这一句翻译为下面的代码:

class print_class
{
public:
	void operator()(void) const
	{
		cout << "Hello World!" << endl;
	}
};
// 用构造的类创建对象,print此时就是一个函数对象
auto print = print_class();

智能指针

智能指针不是指针,是一个管理指针的类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄露。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源。

auto_ptr

c++98定义的智能指针模板

class Test {
public:
	Test() { cout << "Test的构造函数..." << endl; }
	~Test() { cout << "Test的析构函数..." << endl; }

	int getDebug() { return this->debug; }

private:
	int debug = 20;
};

使用智能指针

int main(void) {

	//Test *test = new Test;
	auto_ptr<Test> test(new Test);

	cout << "test->debug:" << test->getDebug() << endl;
	cout << "(*test).debug:" << (*test).getDebug() << endl;

	return 0;
} 

在这里插入图片描述

常用的三个函数

1.get()获取智能指针托管的指针地址

// 定义智能指针
auto_ptr<Test> test(new Test);

Test *tmp = test.get();		// 获取指针返回
cout << "tmp->debug:" << tmp->getDebug() << endl;

但我们一般不会这样使用,因为都可以直接使用智能指针去操作,除非有一些特殊情况。

2.release()取消智能指针对动态内存的托管

// 定义智能指针
auto_ptr<Test> test(new Test);

Test *tmp2 = test.release();	// 取消智能指针对动态内存的托管
delete tmp2;	// 之前分配的内存需要自己手动释放

也就是智能指针不再对该指针进行管理,改由程序员进行管理!

3.reset()重置智能指针托管的内存地址,如果地址不一致,原来的会被析构掉

// 定义智能指针
auto_ptr<Test> test(new Test);

test.reset();			// 释放掉智能指针托管的指针内存,并将其置NULL

test.reset(new Test());	// 释放掉智能指针托管的指针内存,并将参数指针取代之

析构掉原来托管的指针,然后使用参数的指针替代之。然后智能指针就会托管参数的那个指针了。

使用建议

1.尽可能不要将auto_ptr定义为全局变量或指针

// 没有意义,全局变量也是一样
auto_ptr<Test> *tp = new auto_ptr<Test>(new Test);	

2.除非自己知道后果,不要把auto_ptr智能指针赋值给同类型的另一个智能指针

auto_ptr<Test> t1(new Test);
auto_ptr<Test> t2(new Test);
t1 = t2;	// 不要这样操作...

3.c++11后autp_ptr已经被抛弃,已使用unique_ptr代替

复制或者赋值都会改变支援的所有权

// auto_ptr 被C++11抛弃的主要原因
auto_ptr<string> p1(new string("I'm Li Ming!"));
auto_ptr<string> p2(new string("I'm age 22."));

cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

// p2赋值给p1后,首先p1会先将自己原先托管的指针释放掉,然后接收托管p2所托管的指针,
// 然后p2所托管的指针制NULL,也就是p1托管了p2托管的指针,而p2放弃了托管。
p1 = p2;	
cout << "p1 = p2 赋值后:" << endl;
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

在这里插入图片描述

在STL容器中使用auto_ptr存在重大风险,因为容器内的元素必须支持可复制和可赋值

vector<auto_ptr<string>> vec;
auto_ptr<string> p3(new string("I'm P3"));
auto_ptr<string> p4(new string("I'm P4"));

// 必须使用std::move修饰成右值,才可以进行插入容器中
vec.push_back(std::move(p3));
vec.push_back(std::move(p4));

cout << "vec.at(0):" <<  *vec.at(0) << endl;
cout << "vec[1]:" <<  *vec[1] << endl;


// 风险来了:
vec[0] = vec[1];	// 如果进行赋值,问题又回到了上面一个问题中。
cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;

访问越界了

不支持对象数组的内存管理

auto_ptr<int[]> array(new int[5]);	// 不能这样定义

unique_ptr

unique_ptr特性

1.基于排他所有权模式:两个指针不能指向同一个资源

2.无法进行左值unique_ptr复制构造,也无法进行左值赋值操作,但允许临时右值复制构造和复制

3.保存指向某个对象的指针,当它本身离开作用域时会自动释放它指向的对象

4.在容器中保存指针是安全的

无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值

unique_ptr<string> p1(new string("I'm Li Ming!"));
unique_ptr<string> p2(new string("I'm age 22."));
	
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

p1 = p2;					// 禁止左值赋值
unique_ptr<string> p3(p2);	// 禁止左值赋值构造

p1 = std::move(p2);	// 使用move把左值转成右值就可以赋值了,效果和auto_ptr赋值一样
unique_ptr<string> p3(std::move(p1));

cout << "p1 = p2 赋值后:" << endl;
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;

在STL容器中使用unique_ptr,不允许直接赋值

vector<unique_ptr<string>> vec;
unique_ptr<string> p3(new string("I'm P3"));
unique_ptr<string> p4(new string("I'm P4"));

vec.push_back(std::move(p3));
vec.push_back(std::move(p4));

cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;

vec[0] = vec[1];	/* 不允许直接赋值 */
vec[0] = std::move(vec[1]);		// 需要使用move修饰,使得程序员知道后果

cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;

运行后是直接报错的,因为vec[1]已经是NULL了,在继续访问就越界了

支持对象数组的内存管理

// 会自动调用delete [] 函数去释放内存
unique_ptr<int[]> array(new int[5]);	// 支持这样定义

除了上面的三项,unique_ptr的其余用法都与auto_ptr一致

uinque_ptr与auto_ptr智能指针的内存管理陷阱

auto_ptr<string> p1;
string *str = new string("智能指针的内存管理陷阱");
p1.reset(str);	// p1托管str指针
{
	auto_ptr<string> p2;
	p2.reset(str);	// p2接管str指针时,会先取消p1的托管,然后再对str的托管
}

// 此时p1已经没有托管内容指针了,为NULL,在使用它就会内存报错!
cout << "str:" << *p1 << endl;

这是由于auto_ptr与unique_ptr的排他性所导致的

shared_ptr

熟悉了unique_ptr后,其实我们发现unique_ptr这种排他型的内存管理并不能适应所有情况,有很大局限!如果需要多个指针变量共享怎么办?

有一种方式,可以记录引用特定内存对象的智能指针数量,当复制或拷贝时,引用计数+1,当智能指针析构时,引用计数-1,如果计数为零,代表已经没有指针指向这块内存,那么我们就是放它,这就是shared_ptr采用的策略。

class Person {
public:
	Person(int v) {
		this->no = v;
		cout << "构造函数 \t no = " << this->no << endl;
	}

	~Person() {
		cout << "析构函数 \t no = " << this->no << endl;
	}

private:
	int no;
};

// 仿函数,内存删除
class DestructPerson {
public:
	void operator() (Person *pt) {
		cout << "DestructPerson..." << endl;
		delete pt;
	}
};

引用计数的使用

shared_ptr<Person> sp1;

shared_ptr<Person> sp2(new Person(2));

// 获取智能指针管控的共享指针的数量	use_count():引用计数
cout << "sp1	use_count() = " << sp1.use_count() << endl;
cout << "sp2	use_count() = " << sp2.use_count() << endl << endl;

// 共享
sp1 = sp2;

cout << "sp1	use_count() = " << sp1.use_count() << endl;
cout << "sp2	use_count() = " << sp2.use_count() << endl << endl;

shared_ptr<Person> sp3(sp1);
cout << "sp1	use_count() = " << sp1.use_count() << endl;
cout << "sp2	use_count() = " << sp2.use_count() << endl;
cout << "sp2	use_count() = " << sp3.use_count() << endl << endl;

在这里插入图片描述

构造

1.shared_ptr< T > sp1; 空的shared_ptr,可以指向类型为T的对象

shared_ptr<Person> sp1;
Person *person1 = new Person(1);
sp1.reset(person1);	// 托管person1

2.shared_ptr< T > sp2(new T()); 定义shared_ptr,同时指向类型为T的对象

shared_ptr<Person> sp2(new Person(2));
shared_ptr<Person> sp3(sp1);

3.shared_ptr< T > sp7(new T(), D()); //定义shared_ptr,指向类型为T的对象,接受一个D类型的删除器,使用D删除器来释放内存

shared_ptr<Person> sp7(new Person(8), DestructPerson());

初始化

1.构造函数

shared_ptr<int> up1(new int(10));  // int(10) 的引用计数为1
shared_ptr<int> up2(up1);  // 使用智能指针up1构造up2, 此时int(10) 引用计数为2

2.使用make_shared初始化对象,分配内存效率更高(推荐使用)

shared_ptr<int> up3 = make_shared<int>(2); // 多个参数以逗号','隔开,最多接受十个
shared_ptr<string> up4 = make_shared<string>("字符串");
shared_ptr<Person> up5 = make_shared<Person>(9);

赋值

shared_ptr<int> up1(new int(10));  // int(10) 的引用计数为1
shared_ptr<int> up2(new int(11));   // int(11) 的引用计数为1
up1 = up2;	// int(10) 的引用计数减1,计数归零内存释放,up2共享int(11)给up1, int(11)的引用计数为2

主动释放对象

shared_ptr<int> up1(new int(10));
up1 = nullptr ;	// int(10) 的引用计数减1,计数归零内存释放 
// 或
up1 = NULL; // 作用同上 

重置

//p是智能指针,p1是一个指针
p.reset();     //将p重置为空指针,所管理对象引用计数减1
p.reset(p1);   //将p重置为p1(的值),p 管控的对象计数减1,p接管对p1指针的管控
p.reset(p1,d); //将p重置为p1(的值),p 管控的对象计数减1并使用d作为删除器

交换

//p1p2是智能指针
std::swap(p1,p2); // 交换p1 和p2 管理的对象,原对象的引用计数不变
p1.swap(p2);      // 交换p1 和p2 管理的对象,原对象的引用计数不变

shared_ptr使用陷阱

shared_ptr作为被管控的对象的成员时,小心因循环引用造成无法释放资源!

#include <iostream>
#include <string>
#include <memory>

using namespace std;

class Girl;

class Boy {
public:
	Boy() {
		cout << "Boy 构造函数" << endl;
	}

	~Boy() {
		cout << "~Boy 析构函数" << endl;
	}

	void setGirlFriend(shared_ptr<Girl> _girlFriend) {
		this->girlFriend = _girlFriend;
	}

private:
	shared_ptr<Girl> girlFriend;
};

class Girl {
public:
	Girl() {
		cout << "Girl 构造函数" << endl;
	}

	~Girl() {
		cout << "~Girl 析构函数" << endl;
	}

	void setBoyFriend(shared_ptr<Boy> _boyFriend) {
		this->boyFriend = _boyFriend;
	}

private:
	shared_ptr<Boy> boyFriend;
};


void useTrap() {
	shared_ptr<Boy> spBoy(new Boy());
	shared_ptr<Girl> spGirl(new Girl());

	// 陷阱用法
	spBoy->setGirlFriend(spGirl);
	spGirl->setBoyFriend(spBoy);
	// 此时boy和girl的引用计数都是2
}


int main(void) {
	useTrap();

	system("pause");
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

所以在使用shared_ptr智能指针时,要注意避免对象交叉使用智能指针的情况! 否则会导致内存泄露!

如果是单方获得管理对方的共享指针,那么这样是可以正常释放掉的

shared_ptrvoid useTrap() {
	shared_ptr<Boy> spBoy(new Boy());
	shared_ptr<Girl> spGirl(new Girl());

	// 单方获得管理
	//spBoy->setGirlFriend(spGirl);
	spGirl->setBoyFriend(spBoy);	
}

在这里插入图片描述

weak_ptr

weak_ptr设计目的是为了配合shared_ptr而引入的一种智能指针类协助shared_ptr工作,它可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用计数的增加或减少。同时weak_ptr没有重载*和->但可以使用lock获得一个可用的shared_ptr对象。

弱指针使用

shared_ptr<Boy> spBoy(new Boy());
shared_ptr<Girl> spGirl(new Girl());

// 弱指针的使用
weak_ptr<Girl> wpGirl_1;			// 定义空的弱指针
weak_ptr<Girl> wpGirl_2(spGirl);	// 使用共享指针构造
wpGirl_1 = spGirl;					// 允许共享指针赋值给弱指针

cout << "spGirl \t use_count = " << spGirl.use_count() << endl;
cout << "wpGirl_1 \t use_count = " << wpGirl_1.use_count() << endl;

	
// 弱指针不支持 * 和 -> 对指针的访问
/*wpGirl_1->setBoyFriend(spBoy);
(*wpGirl_1).setBoyFriend(spBoy);*/

// 在必要的使用可以转换成共享指针
shared_ptr<Girl> sp_girl;
sp_girl = wpGirl_1.lock();

cout << sp_girl.use_count() << endl;
// 使用完之后,再将共享指针置NULL即可
sp_girl = NULL;

share_ptr中例子的改进

#include <iostream>
#include <string>
#include <memory>

using namespace std;

class Girl;

class Boy {
public:
	Boy() {
		cout << "Boy 构造函数" << endl;
	}

	~Boy() {
		cout << "~Boy 析构函数" << endl;
	}

	void setGirlFriend(shared_ptr<Girl> _girlFriend) {
		this->girlFriend = _girlFriend;


		// 在必要的使用可以转换成共享指针
		shared_ptr<Girl> sp_girl;
		sp_girl = this->girlFriend.lock();

		cout << sp_girl.use_count() << endl;
		// 使用完之后,再将共享指针置NULL即可
		sp_girl = NULL;
	}

private:
	weak_ptr<Girl> girlFriend;
};

class Girl {
public:
	Girl() {
		cout << "Girl 构造函数" << endl;
	}

	~Girl() {
		cout << "~Girl 析构函数" << endl;
	}

	void setBoyFriend(shared_ptr<Boy> _boyFriend) {
		this->boyFriend = _boyFriend;
	}

private:
	shared_ptr<Boy> boyFriend;
};


void useTrap() {
	shared_ptr<Boy> spBoy(new Boy());
	shared_ptr<Girl> spGirl(new Girl());

	spBoy->setGirlFriend(spGirl);
	spGirl->setBoyFriend(spBoy);
}


int main(void) {
	useTrap();

	system("pause");
	return 0;
}

在这里插入图片描述

expired函数的用法

判断当前weak_ptr智能指针是否还有托管的对象,有则返回false,无则返回true

#include <iostream>
#include <memory>

std::weak_ptr<int> gw;

void f() {

	// expired:判断当前智能指针是否还有托管的对象,有则返回false,无则返回true
	if (!gw.expired()) {
		std::cout << "gw is valid\n";	// 有效的,还有托管的指针
	} else {
		std::cout << "gw is expired\n";	// 过期的,没有托管的指针
	}
}

int main() {
	{
		auto sp = std::make_shared<int>(42);
		gw = sp;

		f();
	}

	// 当{ }体中的指针生命周期结束后,再来判断其是否还有托管的指针
	f();

	return 0;
}

在这里插入图片描述

智能指针的使用陷阱

1.不要把一个原生指针给多个智能指针管理

2.记得使用u.release( )的返回值,在调用release( )时是不会释放u所指向的内存的,这时返回值就是对这块内存的唯一索引,如果没有使用这个返回值释放或保存起来,这块内存就释放了

3.禁止delete智能指针get函数返回的指针,如果主动释放掉get函数获得的指针,那么智能指针内部的指针就变成野指针了,析构时造成重复释放,带来严重后果

4.禁止使用任何类型智能指针get函数返回的指针去初始化另一个智能指针

强制类型转化

static_cast

静态转换:可以用于基本数据类型之间的转换,也可以将基类指针或引用转换为派生类指针或引用,但是转换时需要保证类型转换是安全的。静态转换不能用于去除对象的const、volatile属性。

基本数据类型之间的转换

int main()
{
	int a = 5;
	float b = 2.2;
	//printf("%f\n", a);//错误输出
	//printf("%d\n", b);//错误输出
	printf("%f\n", (float)a);
	printf("%d\n", (int)b);
	
	a = static_cast<int>(b);//c++的强转
}

基类和派生类之间的转换(没有virtual)

class A
{
public:
	void fn() { cout << "A::fn" << endl; }
	void gn() { cout << "A::gn" << endl; }
};
class B :public A
{
public:
	void fn() { cout << "B::fn" << endl; }//隐藏,没有虚
	void hn() { cout << "B::hn" << endl; }
};
int main()
{
	A a;
	B b;
	A* pa = &b;
	pa->fn();//A
	pa->gn();//A
//	B* pb = &a;//error
	B* pb = static_cast<B*>(&a);
	pb->fn();//B
	pb->gn();//A
	pb->hn();//B
	pb->A::fn();//A
}

没有关系的类之间转换

class A
{
public:
	void fn() { cout << "A::fn" << endl; }
};
class B
{
public:
	B(A&a){}//构造函数可以进行强转
	void gn() { cout << "B::gn" << endl; }
};
int main()
{
	A a;
	B b = static_cast<B>(a);//B b(a);
	b.gn();
}

dynamic_cast

动态转换:主要用于将基类指针或引用转换为派生类指针或引用,并用派生类的指针或引用来调用非虚函数。但是转换时需要判断类型转换是否安全,如果不安全则返回空指针。只有指向具有多态性质的类的基类指针或引用才能使用dynamic_cast。

class A
{
public:
	void print() { cout << "A::print" << endl; }
	virtual ~A() {};//多态
};
class B :public A
{
public:
	void show() 
	{
		cout << "B::show" << endl;
	}
};
int main()
{
	A* pa = new A;
	B* pb = dynamic_cast<B*>(pa);
	pb->print();//A
	pb->show();//B
}

reinterpret_cast

重新解释转换:将一个指针或引用转换为一个与其类型无关的指针或引用。这种转换不会进行类型检查,因此很容易引发未定义的行为,应避免使用。

int main1()
{
	float ff = 3.5f;
	float* pf = &ff;
	int* pi = reinterpret_cast<int*>(pf);//编译时重新解释
	cout << *pi << endl;//取不到正确的值
	cout << *pf << endl;//pf不变
 
	//整形按二进制补码存
	unsigned int a = -2;//把-2放到a开辟的空间中
	/*
	00000000 00000000 00000000 00000010
	11111111 11111111 11111111 11111110:-2,4294967294
	unsigned无符号位
	*/
	//浮点sem存
}
 
class A
{
 
};
class B
{
 
};
int main()
{
	A* pa = new A;
	B* pb = reinterpret_cast<B*>(pa);
	//B*pb = (B*)pa;
	int a = 10;
	int* pi = &a;
	long j = reinterpret_cast<long>(pi);
	cout << j << endl;//地址值
}

const_cast

常量转换:用于去除一个变量或指针的const、volatile属性。这种转换也需要谨慎使用,因为去除const属性可能会导致未定义的行为。

int main()
{
    const int cb = 10;
    //int* p = &cb;
    //int* p = static_cast<int*>(&cb);//error
    int* p = const_cast<int*>(&cb);//
    *p = 30;
    cout << cb << endl;//cb在字符串常量区,改不了,输出10
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值