C/C++中有几种指针相关的概念,只知道有这样的概念,但HR一问就露馅,这里进行总结方便复习。
1、指针定义时未被初始化:指针在被定义的时候,如果程序不对其进行初始化的话,它会指向随机区域,因为任何指针变量(除了static修饰的指针变量)在被定义的时候是不会被置空的,它的默认值是随机的。
2、指针被释放时没有被置空:我们在用malloc开辟内存空间时,要检查返回值是否为空,如果为空,则开辟失败;如果不为空,则指针指向的是开辟的内存空间的首地址。指针指向的内存空间在用free()或者delete(注意delete只是一个操作符,而free()是一个函数)释放后,如果程序员没有对其置空或者其他的赋值操作,就会使其成为一个野指针。
3、指针操作超越变量作用域:不要返回指向栈内存的指针或引用,因为栈内存在函数结束的时候会被释放。
//1、指针定义时未被初始化
int main(void) {
int* p; // 未初始化
cout<< *p <<" 俺是个野指针!" << endl; // 未初始化就被使用
return 0;
}
/*
(无输出)
*/
//指针操作超越变量作用域
class A {
public:
int num=2;
void Func(void){
// num=2;
cout << "Func of class A "<< endl;
}
};
class B{
public:
A *p;
void Test(void){
A a;
p = &a; // 注意a的生命期,只在这个函数Test中,而不是整个class B
}
void Test1(){
p->Func(); // p 是“野指针”,输出(成员函数是类中的):Func of class A
p->num; // p 是“野指针”,无输出(没有实例化对象)
}
};
野指针的危害:
解引用一个非空的无效野指针是一个未被定义的行为,也就是说不一定导致段错误,野指针很难定位到是哪里出现的问题,在哪里这个指针就失效了,不好查找出错的原因。所以调试起来会很麻烦,有时候会需要很长的时间。
避免办法:
(1)初始化置NULL
(2)申请内存后判空
(3)指针释放后置NULL
(4)使用智能指针
#define NULL 0 //c语言中,是整数0
#define NULL ((void *)0) //c++中,是无符号指针0
const class nullptr_t{...} nullptr = {}; //c++中,是nullptr_t类型的关键字(c++11新增)
NULL当做整数0来看;nullptr可以明确区分整型和指针类型,能够根据环境自动转换成相应的指针类型,但不会被转换为任何整型,所以不会造成参数传递错误。
1.nullptr通过模板类和运算符重载的方式来对不同类型的指针进行实例化从而解决了(void*)指针带来参数指针类型不明的问题;
2.nullptr是明确的指针类型,所以不会与整形变量相混淆;
3.但nullptr仍然存在不同类型指针混淆问题。
#include
using namespace std;
void fun(char* p){
cout<< "char* p" <<endl;
}
void fun(int* p){
cout<< "int* p" <<endl;
}
void fun(int p){
cout<< "int p" <<endl;
}
int main(){
fun((char*)nullptr);//语句1,输出:char* p
//fun(nullptr); //语句2,有多个(1,2)重载函数 "fun" 实例与参数列表匹配
//fun(NULL); //语句3,有多个(1,2,3)重载函数 "fun" 实例与参数列表匹配
fun(0); //语句4,输出:int p
return 0;
}
野指针和空指针都是指向无效(不安全不可控)内存区域的指针,访问行为将会导致未定义行为。
指针最初指向的内存已经被释放了的一种指针。
int main(void) {
int * p = nullptr;
int* p2 = new int;
p = p2;
delete p2;
cout<< *p<< endl; // 悬空
return 0;
}
/*
//多次启动运行结果不确定:
7299040
6905824
17457120
15622112
*/
此时 p和p2就是悬空指针,指向的内存已经被释放。继续使用这两个指针,行为不可预料。需要设置为p=p2=nullptr。此时再使用,编译器会直接报错。 避免野指针比较简单,但悬空指针比较麻烦。c++引入了智能指针,C++智能指针的本质就是避免悬空指针的产生。
产生原因及解决办法:
1.野指针:指针变量未及时初始化 => 定义指针变量及时初始化,要么置空。
2.悬空指针:指针free或delete之后没有及时置空 => 释放操作后立即置空。
在C++11中通过引入智能指针的概念,使得C++程序员不需要手动释放内存。智能指针是原始指针的封装,其优点是会自动分配与释放内存,不用担心潜在的内存泄露问题,也不用担心潜在的内存重复释放引起运行程序崩溃问题。
智能指针的种类:
#include
std::unique_ptr
std::shared_ptr
std::weak_ptr
//std::ato_ptr已经被废弃
声明与使用:
#include
#include
#include "cat.h" //cat.h文件在本文的后边
using namespace std;
int main(int argc, char *argv[]){
//第一种定义:
// std::unique_ptr u_c_p2{new Cat("yz")};
//第二种定义:
std::unique_ptr<Cat> u_c_p4 = make_unique<Cat>("xx");
std::unique_ptr<int> u_i_p4 = make_unique<int>(200);
u_c_p4->cat_info();
u_c_p4->set_cat_name("oo");
u_c_p4->cat_info();
cout << *u_i_p4 << endl;
cout << "int address: " << u_i_p4.get() << endl;
cout << "cat address: " << u_c_p4.get() << endl;
cout<<"-----------------------"<<endl;
return 0;
}
/*输出
Constructor of Cat : xx
cat info name :xx
cat info name :oo
200
int address: 0x6a1c90
cat address: 0x6a1c60
-----------------------
Destructor of Cat : oo
*/
作为参数被传递:
值传递:
需要用std::move来转移内存拥有权(如果参数直接传入std::make unique语句 自动转换为move),及作用范围变为函数的局部变量;
引用传递:
如果设置参数为const则不能改变指向比方说reset(),reset0)方法为智能指针清空方法;
函数返回:
指向一个local object可以用作链式函数。
#include
#include
#include "cat.h" //cat.h文件在本文的后边
using namespace std;
void do_with_cat_pass_value(std::unique_ptr<Cat> c){
c->cat_info();
}
void do_with_cat_pass_ref(const std::unique_ptr<Cat> &c){
c->set_cat_name("oo");
c->cat_info();
// c.reset();//销毁内存,指针指0;不能与const一起用
}
std::unique_ptr<Cat> get_unique_ptr(){
std::unique_ptr<Cat> p_dog = std::make_unique<Cat>("Local cat");
cout << "数据地址: " << p_dog.get() << " 指针指针: " << &p_dog << endl;
return p_dog;
}
int main(int argc, char *argv[]){
// 1 pass value
std::unique_ptr<Cat> c1 = make_unique<Cat>("ff");
do_with_cat_pass_value(std::move(c1));
// c1->cat_info();// make unique
do_with_cat_pass_value(std::make_unique<Cat>()); // move
cout<<"-----------------------"<<endl;
// 2 pass ref
// 不加const
std::unique_ptr<Cat> c2 = make_unique<Cat>("f2");
do_with_cat_pass_ref(c2);
// c2->cat_info();
cout << "数据地址 : " << c2.get() << " 指针地址 : "<< &c2 << endl;
c2->cat_info();
cout<<"-----------------------"<<endl;
// 3链式
get_unique_ptr()->cat_info();
cout<<"-----------------------"<<endl;
return 0;
}
/*输出
Constructor of Cat : ff
cat info name :ff
Destructor of Cat : ff
cat info name :Mimi
Destructor of Cat : Mimi
-----------------------
Constructor of Cat : f2
cat info name :oo
数据地址 : 0xf91c60 指针地址 : 0x61fd18
cat info name :oo
-----------------------
Constructor of Cat : Local cat
数据地址: 0xf91c90 指针指针: 0x61fd38
cat info name :Local cat
Destructor of Cat : Local cat
-----------------------
Destructor of Cat : oo
*/
shared_ptr 计数指针又称共享指针,与unique_ptr不同的是它是可以共享数据的。
shared_ptr创建了一个计数器与类对象所指的内存相关联,Copy则计数器加一,销毁则计数器减一,api为use_count()。
定义与使用:
#include
#include
#include "cat.h" //cat.h文件在本文的后边
using namespace std;
int main(int argc, char *argv[]){
//常量类型
std::shared_ptr<int> i_p_1 = make_shared<int>(10);
// std::shared_ptr i_p_1 = make_shared{new int(10)};
cout << "数值: "<< *i_p_1 << endl;
cout <<"i_p_1的共享指针数量:"<< i_p_1.use_count() << " 数据&指针地址:"
<< i_p_1.get() << " " << &i_p_1 << endl;
cout<<"-----------------------"<<endl;
// copy
std::shared_ptr<int> i_p_2 = i_p_1;
cout <<"i_p_1的共享指针数量:"<< i_p_1.use_count() << " 数据&指针地址:"
<< i_p_1.get() << " " << &i_p_1 << endl;
cout <<"i_p_2的共享指针数量:"<< i_p_2.use_count() << " 数据&指针地址:"
<< i_p_2.get() << " " << &i_p_2 << endl;
cout<<"-----------------------"<<endl;
//change
*i_p_2 = 30;
cout <<"i_p_1的共享指针数量:"<< i_p_1.use_count() << " 数据&指针地址:"
<< i_p_1.get() << " " << &i_p_1 << endl;
cout <<"i_p_2的共享指针数量:"<< i_p_2.use_count() << " 数据&指针地址:"
<< i_p_2.get() << " " << &i_p_2 << endl;
cout<<"-----------------------"<<endl;
// ip 2 = nullptr;
std::shared_ptr<int> i_p_3 = i_p_1;
i_p_1 = nullptr;
cout <<"i_p_1的共享指针数量:"<< i_p_1.use_count() << " 数据&指针地址:"
<< i_p_1.get() << " " << &i_p_1 << endl;
cout <<"i_p_2的共享指针数量:"<< i_p_2.use_count() << " 数据&指针地址:"
<< i_p_2.get() << " " << &i_p_2 << endl;
cout <<"i_p_3的共享指针数量:"<< i_p_3.use_count() << " 数据&指针地址:"
<< i_p_3.get() << " " << &i_p_3 << endl;
cout<<"-----------------------"<<endl;
//自定义类型
std::shared_ptr<Cat> c_p_1 = make_shared<Cat>();
cout <<"c_p_1的共享指针数量:"<< c_p_1.use_count() << " 数据&指针地址:"
<< c_p_1.get() << " " << &c_p_1 << endl;
std::shared_ptr<Cat> c_p_2 = c_p_1;
std::shared_ptr<Cat> c_p_3 = c_p_1;
cout <<"c_p_1的共享指针数量:"<< c_p_1.use_count() << " 数据&指针地址:"
<< c_p_1.get() << " " << &c_p_1 << endl;
cout <<"c_p_2的共享指针数量:"<< c_p_2.use_count() << " 数据&指针地址:"
<< c_p_2.get() << " " << &c_p_2 << endl;
cout <<"c_p_3的共享指针数量:"<< c_p_3.use_count() << " 数据&指针地址:"
<< c_p_3.get() << " " << &c_p_3 << endl;
cout<<"-----------------------"<<endl;
c_p_1.reset();
cout <<"c_p_1的共享指针数量:"<< c_p_1.use_count() << " 数据&指针地址:"
<< c_p_1.get() << " " << &c_p_1 << endl;
cout <<"c_p_2的共享指针数量:"<< c_p_2.use_count() << " 数据&指针地址:"
<< c_p_2.get() << " " << &c_p_2 << endl;
cout <<"c_p_3的共享指针数量:"<< c_p_3.use_count() << " 数据&指针地址:"
<< c_p_3.get() << " " << &c_p_3 << endl;
cout<<"-----------------------"<<endl;
return 0;
}
/*输出
数值: 10
i_p_1的共享指针数量:1 数据&指针地址:0xde1c70 0x61fd10
-----------------------
i_p_1的共享指针数量:2 数据&指针地址:0xde1c70 0x61fd10
i_p_2的共享指针数量:2 数据&指针地址:0xde1c70 0x61fd00
-----------------------
i_p_1的共享指针数量:2 数据&指针地址:0xde1c70 0x61fd10
i_p_2的共享指针数量:2 数据&指针地址:0xde1c70 0x61fd00
-----------------------
i_p_1的共享指针数量:0 数据&指针地址:0 0x61fd10
i_p_2的共享指针数量:2 数据&指针地址:0xde1c70 0x61fd00
i_p_3的共享指针数量:2 数据&指针地址:0xde1c70 0x61fcf0
-----------------------
c_p_1的共享指针数量:1 数据&指针地址:0xde1c90 0x61fce0
c_p_1的共享指针数量:3 数据&指针地址:0xde1c90 0x61fce0
c_p_2的共享指针数量:3 数据&指针地址:0xde1c90 0x61fcd0
c_p_3的共享指针数量:3 数据&指针地址:0xde1c90 0x61fcc0
-----------------------
c_p_1的共享指针数量:0 数据&指针地址:0 0x61fce0
c_p_2的共享指针数量:2 数据&指针地址:0xde1c90 0x61fcd0
c_p_3的共享指针数量:2 数据&指针地址:0xde1c90 0x61fcc0
-----------------------
Destructor of Cat : Mimi
*/
注意:shared_ptr只会析构一次,c_p_1=nullptr与c_p_1.reset()效果相同。
作为参数被传递:
shared_ptr通过值传递:copy函数内部计数器加一
shared_ptr通过引用传递:const表示不可改变指向
作为函数返回值:链式调用
#include
#include
#include "cat.h" //cat.h文件在本文的后边
using namespace std;
void cat_by_value(std::shared_ptr<Cat> cat){
cout << cat->get_name() << endl;
cat->set_cat_name("ee");
cout << "cat的共享指针数量:" << cat.use_count() << " 数据&指针地址:"
<< cat.get() << " " << &cat << endl;
}
void cat_by_ref(std::shared_ptr<Cat> &cat){
cout << cat->get_name() << endl;
// cat.reset(new Cat);//new Cat是销毁之前的对象,新建对象,防止代码崩溃
cout << "cat的共享指针数量:" << cat.use_count() << " 数据&指针地址:"
<< cat.get() << " " << &cat << endl;
}
std::shared_ptr<Cat> get_shared_ptr(string s){
std::shared_ptr<Cat> cat_p = std::make_shared<Cat>(s);
return cat_p;
}
int main(int argc, char *argv[]){
//值传递
std::shared_ptr<Cat> c1 = make_shared<Cat>("dd");
cat_by_value(c1);
c1->cat_info();
cout << "c1的共享指针数量:" << c1.use_count() << " 数据&指针地址:"
<< c1.get() << " " << &c1 << endl;
cout<<"-----------------------"<<endl;
//引用传递
cat_by_ref(c1);
c1->cat_info();
// cout << "c1的共享指针数量:" << c1.use_count() << " 数据&指针地址:"
// << c1.get() << " " << &c1 << endl;
cout<<"-----------------------"<<endl;
//返回值
std::shared_ptr<Cat> c_p = get_shared_ptr("local cat1");
c_p->cat_info();
cout<<"-----------------------"<<endl;
get_shared_ptr("local cat2")->cat_info();
cout<<"-----------------------"<<endl;
return 0;
}
/*输出
Constructor of Cat : dd
dd
cat的共享指针数量:2 数据&指针地址:0x6b2760 0x61fcc0
cat info name :ee
c1的共享指针数量:1 数据&指针地址:0x6b2760 0x61fcb0
-----------------------
ee
cat的共享指针数量:1 数据&指针地址:0x6b2760 0x61fcb0
cat info name :ee
-----------------------
Constructor of Cat : local cat1
cat info name :local cat1
-----------------------
Constructor of Cat : local cat2
cat info name :local cat2
Destructor of Cat : local cat2
-----------------------
Destructor of Cat : local cat1
Destructor of Cat : ee
*/
不能将shared_ptr转换为unique_ptr,但可以通过std::move将unique_ptr转换为shared_ptr。函数返回unique_ptr是一种常见的设计模式,这样可以随时改变为shared_ptr,提高代码的复用度。
#include
#include
#include "cat.h" //cat.h文件在本文的后边
using namespace std;
std::unique_ptr <Cat> get_unique_ptr(){
std::unique_ptr<Cat> cat_p = std::make_unique<Cat>("local cat");
cout <<"cat_p" << " 数据&指针地址:"<< cat_p.get() << " " << &cat_p << endl;
return cat_p;
}
int main(int argc, char *argv[]){
std::unique_ptr<Cat> c_p_1 = std::make_unique<Cat>("dd");
std::shared_ptr<Cat> c_p_2 = std::move(c_p_1);
cout <<"c_p_1" << " 数据&指针地址:"<< c_p_1.get() << " " << &c_p_1 << endl;
cout <<"c_p_2的共享指针数量:"<< c_p_2.use_count() << " 数据&指针地址:"
<< c_p_2.get() << " " << &c_p_2 << endl;
cout<<"-----------------------"<<endl;
// func
std::shared_ptr<Cat> c_p_3 = get_unique_ptr();
if (c_p_3){
c_p_3->cat_info();
cout <<"c_p_3的共享指针数量:"<< c_p_3.use_count() << " 数据&指针地址:"
<< c_p_3.get() << " " << &c_p_3 << endl;
}
cout<<"-----------------------"<<endl;
return 0;
}
/*输出
Constructor of Cat : dd
c_p_1 数据&指针地址:0 0x61fd30
c_p_2的共享指针数量:1 数据&指针地址:0x25a2750 0x61fd20
-----------------------
Constructor of Cat : local cat
cat_p 数据&指针地址:0x25a2810 0x61fd38
cat info name :local cat
c_p_3的共享指针数量:1 数据&指针地址:0x25a2810 0x61fd10
-----------------------
Destructor of Cat : local cat
Destructor of Cat : dd
*/
当A类中有一个需求需要存储其他A类对象的信息,如果使用shared_ptr在销毁时会遇到循环依赖问题 (Cyclic dependency problem),所以需要用一个不需要拥有所有权的指针来标记该同类对象weak_ptr(weak_ptr可以通过lock()函数来提升为shared_ptr(类型转换))。
weak_ptr并不拥有所有权,并不能调用 -> 和解引用*。
#include
#include
#include "cat.h" //cat.h文件在本文的后边
using namespace std;
// std::unique_ptr get_unique_ptr(){
// std::unique_ptr cat_p = std::make_unique("local cat");
// cout <<"cat_p" << " 数据&指针地址:"<< cat_p.get() << " " << &cat_p << endl;
// return cat_p;
// }
int main(int argc, char *argv[]){
//shared_ptr -> weak_ptr
std::shared_ptr<Cat> s_p_c1 = std::make_shared<Cat>("C1");
std::weak_ptr<Cat> w_p_c1(s_p_c1);
cout <<"s_p_c1的共享指针数量:"<< s_p_c1.use_count() << " 数据&指针地址:"
<< s_p_c1.get() << " " << &s_p_c1 << endl;
cout <<"w_p_c1的共享指针数量:"<< w_p_c1.use_count() << " 数据&指针地址:"
<< "无" << " " << &w_p_c1 << endl;
cout<<"-----------------------"<<endl;
//weak_ptr -> shared_ptr
std::shared_ptr<Cat> s_p_c2 = w_p_c1.lock();
cout <<"s_p_c1的共享指针数量:"<< s_p_c1.use_count() << " 数据&指针地址:"
<< s_p_c1.get() << " " << &s_p_c1 << endl;
cout <<"w_p_c1的共享指针数量:"<< w_p_c1.use_count() << " 数据&指针地址:"
<< "无" << " " << &w_p_c1 << endl;
cout <<"s_p_c2的共享指针数量:"<< s_p_c2.use_count() << " 数据&指针地址:"
<< s_p_c2.get() << " " << &s_p_c2 << endl;
cout<<"-----------------------"<<endl;
//循环依赖
std::shared_ptr<Cat> c3 = std::make_shared<Cat>("C3");
std::shared_ptr<Cat> c4 = std::make_shared<Cat>("C4");
c3->set_friend(c4);
c4->set_friend(c3);
cout<<"-----------------------"<<endl;
return 0;
}
/*输出
Constructor of Cat : C1
s_p_c1的共享指针数量:1 数据&指针地址:0xea1c70 0x61fd10
w_p_c1的共享指针数量:1 数据&指针地址:无 0x61fd00
-----------------------
s_p_c1的共享指针数量:2 数据&指针地址:0xea1c70 0x61fd10
w_p_c1的共享指针数量:2 数据&指针地址:无 0x61fd00
s_p_c2的共享指针数量:2 数据&指针地址:0xea1c70 0x61fcf0
-----------------------
Constructor of Cat : C3
Constructor of Cat : C4
-----------------------
Destructor of Cat : C4
Destructor of Cat : C3
Destructor of Cat : C1
*/
cat.h文件
//cat.h文件
#ifndef CAT_H
#define CAT_H
#include
#include
class Cat{
public:
Cat(std::string name1): name(name1){
std::cout << "Constructor of Cat : "<< name << std::endl;
}
Cat() = default;
~Cat(){
std::cout << "Destructor of Cat : " << name << std::endl;
}
void cat_info() const{
std::cout << "cat info name :"<< name << std::endl;
}
std::string get_name() const{
return name;
}
void set_cat_name(const std::string &name){
this->name = name;
}
void set_friend(std::shared_ptr<Cat> c){
m_friend = c;
}
private:
std::string name{"Mimi"};
std::weak_ptr<Cat> m_friend;
};
#endif
https://interviewguide.cn/notes/03-hunting_job/02-interview/01-01-02-basic.html#_31%E3%80%81%E9%87%8E%E6%8C%87%E9%92%88%E5%92%8C%E6%82%AC%E7%A9%BA%E6%8C%87%E9%92%88
https://www.cnblogs.com/asking/p/9460965.html
http://c.biancheng.net/view/2016.html
https://blog.csdn.net/N1neDing/article/details/83088113?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-83088113-blog-46629065.pc_relevant_3mothn_strategy_and_data_recovery&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-83088113-blog-46629065.pc_relevant_3mothn_strategy_and_data_recovery&utm_relevant_index=2
https://www.bilibili.com/video/BV18B4y187uL?p=1&vd_source=07c19be7e433a8cd75d9808899f4c9d9