跳至主要內容

指针

Entity大约 8 分钟

指针

指针常量vs常量指针

int a = 10;
// 常量指针
const int * b = &a;
// 指针常量
int * const c = &a;

指针常量

特点:指针的指向不可以修改,指针指向的内存的值可以修改

  • 什么意思?
    • 变量指向的地址不可以更改,变量指向的地址的值可以修改
    int a = 10;
    int * const b = &b;
    *b = 20; // input :  20
    int c = 12;
    b = &c; // error , 不可以修改指向
    

常量指针

特点:指针的指向可以修改,但是指针指向的值不可修改

int a = 10;
const int * b = &a;
*b = 20; // error ,指针指向内容的值不可修改
int c = 12;
b = &c; // ok,可以修改指针的指向

指向常量的常指针

特点:指针的指向不可以修改,指针指向的值也不可更改

int a = 10;
const int * const b = &a;

int c = 12;
b = &c; // error
*b = 20; // error

空指针、野指针

空指针就是指向地址为空的指针

int * ptr = NULL;

野指针就是指针指向一个随意的内存地址,如果使用,会造成不可预知的内存错误

int * ptr = 0x1100; // 任意赋值的野指针

// 删除指针
int * ptr = new int(5);
delete ptr;
ptr = NULL;

函数后面跟着const

在函数后面加const表示函数不可以修改class的成员

class test{
    public:
    int val;
    void setVal() const {
        val = 10; // error,不可以修改class成员
    }
    // 前面有const表示返回的值是const类型的
    const int GetVal(){
        const int temp = val;
        return temp;
    }
}

函数指针

是指向函数的指针变量,即本质是一个指针变量

函数指针的定义为:类型说明符 (*函数名) (参数),例如:int (*P) (int x);

void (*func)(int x);
// 
void funcTest(int x);
func = funcTest;
(*func)();

指针函数

是指函数返回值是某一类型的指针,本质是一个函数

指针函数的定义为: 类型标识符 *函数名(参数表), 例如: int *f(x,y);

int *func();

int *a = func();

智能指针

  • 为什么要用智能指针?
    • 智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针是一个类,当超出了类的实例对象的作用域时,会自动调用对象的析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。
智能指针说明
shared_ptrC++ 11中最常用的智能指针类型为shared_ptr,它采用引用计数的方法,记录当前内存资源被多少个智能指针引用。该引用计数的内存在堆上分配。当新增一个时引用计数加1,当过期时引用计数减一。只有引用计数为0时,智能指针才会自动释放引用的内存资源。对shared_ptr进行初始化时不能将一个普通指针直接赋值给智能指针,因为一个是指针,一个是类。可以通过make_shared函数或者通过构造函数传入普通指针。并可以通过get函数获得普通指针
auto_ptrc++11已经抛弃,存在潜在的内存崩溃问题
unique_ptr(替换auto_ptr)unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露(例如“以new创建对象后因为发生异常而忘记调用delete”)特别有用。

使用智能指针

使用智能指针的时候需要加上头文件memory

#include <memory>
using namespace std;

unique_ptr

当程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做,比如:

unique_ptr<string> p3(new string("auto"));
unique_ptr<string> p4;
p4 = p3; // 会报错

p4 = unique_ptr<string>(new string("new")); // 允许

shared_ptr

shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。

shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。

成员函数

函数说明
use_count返回引用计数的个数
unique返回是否独占所有权(ues_count为1)
swap交换两个shared_ptr对象(即交换所拥有的对象)
reset放弃内部对象的所有权或拥有对象的变更,会引起源对象的引用计数减少
get返回内部对象(指针),由于已经重载了方法,因此和直接使用对象是一样的

shared_ptr的简单例子

int main(){
    string *s1 = new string("s1");

    shared_ptr<string> ps1(s1);
    shared_ptr<string> ps2;
    ps2 = ps1;

    cout << ps1.use_count() << endl; // 2
    cout << ps2.use_count() << endl; // 2
    cout << ps1.unique() << endl; // 0

    cout << s1 << endl; // 0x00000
    cout << *s1 << endl; // s1

}

使用make_shared创建指针

尽量使用make_shared初始化智能指针

shared_ptr s1;
s1 = make_shared<string>("你好世界~");

cout << *s1 << endl; // 你好世界~

weak_ptr

该类型的指针通常不单独使用(没有实际用处),只能和shared_ptr类型指针搭配使用。甚至于,我们可以将weak_ptr类型指针视为shared_ptr指针的一种辅助工具。

当你想使用对象,但是并不想管理对象,并且在需要使用对象时可以判断对象是否还存在

class B; // 前置声明类B
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	weak_ptr<B> _ptrb; // 指向B对象的弱智能指针。引用对象时,用弱智能指针
};
class B
{
public:
	B() { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
	weak_ptr<A> _ptra; // 指向A对象的弱智能指针。引用对象时,用弱智能指针
};
int main()
{
    // 定义对象时,用强智能指针
	shared_ptr<A> ptra(new A());// ptra指向A对象,A的引用计数为1
	shared_ptr<B> ptrb(new B());// ptrb指向B对象,B的引用计数为1
	
    // A对象的成员变量_ptrb也指向B对象,B的引用计数为1,因为是弱智能指针,引用计数没有改变
	ptra->_ptrb = ptrb;
	// B对象的成员变量_ptra也指向A对象,A的引用计数为1,因为是弱智能指针,引用计数没有改变
	ptrb->_ptra = ptra;

	cout << ptra.use_count() << endl; // 打印结果:1
	cout << ptrb.use_count() << endl; // 打印结果:1

	/*
	出main函数作用域,ptra和ptrb两个局部对象析构,分别给A对象和
	B对象的引用计数从1减到0,达到释放A和B的条件,因此new出来的A和B对象

属性

属性说明
operator=()重载=赋值运算符,是的weak_ptr指针可以直接被weak_ptr或者shard_ptr类型指针赋值。
swap(x)其中x表示一个同类型的weak_ptr类型指针, 该函数可以互换2个同类型weak_ptr指针的内容
reset()将当前指针重置为空指针
use_count()查看指向和当前weak_ptr相同的shared_ptr指针数量
expired()判断当前指针是否过期
lock()如果当前weak_ptr已经过期

删除指针

int * ptr = (int *)234;
delete ptr;

// 管理动态数组时,需要指定删除器,shared_ptr默认的删除器(使用的是delete)不支持数组对象。
shared_ptr<int> ptr(new int[1],default_delete<int[]>());
// or,第二个参数是lambda表达式
shared_ptr<int> ptr2(new int[1],[](int *p){delete[] p;});

判断指针是否为空

if(bool(ptr)){
    cout << "ptr is nullptr" << endl;
}else{
    cout << "the ptr is not nullptr" << endl;
}

注意事项

不用使用原始指针初始化多个shared_ptr,否则会造成二次释放同一个内存

int * p = new int;
std::shared_ptr<int> p2(p);
std::shared_ptr<int> p3(p);

不要在实参中创建shared_ptr,应该先创建一个智能指针,再使用

deleteIntPtr(shd::shared_ptr<int>(new int)); // 错误的
shd::shared_ptr<int> ptr(new int);
deleteIntPtr(ptr);  // ok

返回this指针要通过shared_from_this();

struct A
{
    std::shared_ptr<A> getSelf(){
        return std::shared_ptr<A>(this); // 错误
    }
};
int main(){
    std::shared_ptr<A> p(new A);
    std::shared_ptr<A> p2 = p->getSelf(); // 会导致重复析构
}

应采用如下方式,继承enable_shared_from_this类的shared_from_this()方法返回this指针

class A: public std::enable_shared_from_this
{
    public:
        std::shared_ptr<A> getSelf(){
            return shared_from_this();
        }
}

将DLL注入到其他进程,并使用基址+偏移获取属性的内容

// 使用ce获取的基址
int* baseAddress = (int*)(0x0019975C);
// baseAddress是指针,并且它的值也是指针
int* pointer = (int*)*baseAddress;
// 偏移量
int deviation = 0x5578;
std::count << "" << pointer + deviation << std::endl;
*(pointer + deviation) = 999;
int* inject = (int*)(0x0019975C);
int* two = (int*)*inject;
int off = 0x5578;
std::cout << "" << (int*)(*inject + off) << std::endl;
std::cout << "" << (int*)(*inject + 0x5578) << std::endl;
*(int*)(*inject + off) = 999;