指针与引用

1. 指针与引用

类型 储存 初始化 二次赋值 形式 sizeof
指针 有储存空间 NULL 可以 类名 * 指针的大小
引用 无空间,别名 不能为空 不行 类名 & 类型的大小

[!note|style:flat]

  • c语言没有引用机制,是靠c++扩展实现。

2. 引用

2.1. 引用的初始化

[!note|style:flat] 引用的创建不会调用类的拷贝构造函数

2.1.1. 函数变量

直接在定义变量的时候初始化:

int b = 1;
int & a = b;

2.1.2. 类变量初始

[!note|style:flat] 使用「初始化列表」进行初始化。

class Test{
public:
    int & a;

    Test(int &a) : a(a) {

    } 

};

2.2. const引用

2.2.1. 概念介绍

[!tip]

  • const int & d2 = d1 对于d2只是d1的别名;名义上d2并没有分配,内存地址与d1一摸一样;
  • d2的值」并没有被放到「符号表」;d1修改后,d2的值也更着改变
int d1 = 4;
const int & d2 = d1;
d1 = 5; // 此时,d2的值也是:5

2.2.2. 常量引用

[!tip|style:flat] 常量具有常性,只有const引用可以引用常量

const int & d1 = 5;

2.2.3. 隐蔽类型转换的引用

[!note|style:flat] d1double类型,d2intd1赋值给 d2时,要先生成一个「带常性的临时变量」,然后用临时变量进行赋值,所以int& d2 = d1;错误。

double d1 = 1.1// 错误
int& d2 = d1; 

// 正确
const int& d3 = d2;

2.3. 引用返回

  1. 不能返回局部变量的引用
  2. 不能返回函数内部new分配的内存的引用,利用引用返回,就没法释放内存了。

3. 智能指针

类型 作用
std::auto_ptr 废弃
unique_ptr 对象只能一个指针持有
shared_ptr 多个指针指向相同的对象
weak_ptr 用于防止shared_ptr循环引用

[!note]

  • 通过库函数,管理对象指针
  • ptr.:调用的是智能指针对象的内容
  • ptr->:调用的是智能指针所指向对象的内容

1. unique_ptr

作用:对象只能一个指针持有。

// 初始化赋值
std::unique_ptr<int> p1(new int(5));

// 移交所有权,p1将变无效
std::unique_ptr<int> p2 = std::move(p1);

// 滞后赋值
unique_ptr<string> p3;
p3 = unique_ptr<string>(new string ("You"));

// 获取指向对象的地址
p3.get();

2. shared_ptr

作用:多个指针指向相同的对象。内部会有一个指向计数器,当计数器变为0时,系统就会对这个对象进行销毁,线程安全。

// make_shared生成
std::shared_ptr<int> p1 = std::make_shared<int>(10);

// 拷贝赋值
std::shared_ptr<int> p2;
p2 = p1;

// 获取引用计数
p2.use_count();

计数器temp = p;赋值时,两端的计数器变化

  • temp之前指向对象的计数器会「递减」
  • p指向对象的计数器会「递增」
  • 赋值后,temp指向了p的对象,计数器也就和p一样了
shared_ptr<Parent> r = make_shared<Parent>("r",45); 
shared_ptr<Parent> p = make_shared<Parent>("p", 50);
shared_ptr<Parent> temp;

temp = r;
cout << p.use_count() << " " << p->name << endl;
cout << r.use_count() << " " << r->name << endl;

temp = p;
cout << p.use_count() << " " << p->name << endl;
cout << r.use_count() << " " << r->name << endl;

triangle@LEARN_FUCK:~$ make 1 p 2 r 2 p 1 r

3. weak_ptr

作用:没有重载operator*->,用于防止shared_ptr循环引用。

weak_ptr<> wp;

// 作用等同于 wp.use_count() == 0
wp.expired();

// 返回一个共享指针
shared_ptr<> sp = wp.lock();
案例代码
#include <iostream>
#include <memory>
using namespace std;

class A{
public:
    int a;
    int c; 
};


int main(int argc, char const *argv[])
{
    weak_ptr<A> wa;
    shared_ptr<A> sa = make_shared<A>();

    wa = sa; 

    if(!wa.expired()){
        shared_ptr<A> temp;
        temp = wa.lock();
        temp->a = 10;
    }

    cout << sa->a << endl;
    return 0;
}

循环引用:

#include <iostream>
#include <memory>
using namespace std;

class A;
class B;


class A{
public:
    shared_ptr<B> b;
};


class B{
public:
    shared_ptr<A> a;
};


int main(int argc, char const *argv[])
{
    weak_ptr<A> wa;
    weak_ptr<B> wb;
    {
        shared_ptr<A> sa = make_shared<A>();
        shared_ptr<B> sb = make_shared<B>();

        // 循环引用
        sa->b = sb;
        sb->a = sa;

        wa = sa;
        wb = sb; 
    }

    cout << "sa: " << wa.use_count() << endl;
    cout << "sb: " << wb.use_count() << endl;

    return 0;
}

[!note|style:flat] 由于shared_ptr的循环引用,造成「内存泄露」

triangle@LEARN_FUCK:~$ make sa: 1 sb: 1 ================================================================= ==991==ERROR: LeakSanitizer: detected memory leaks Indirect leak of 32 byte(s) in 1 object(s) allocated from: #8 0x7f9fd200275f in std::shared_ptr<B> std::make_shared<B>() /usr/include/c++/7/bits/shared_ptr.h:707 Indirect leak of 32 byte(s) in 1 object(s) allocated from: #8 0x7f9fd20025c5 in std::shared_ptr<A> std::make_shared<A>() /usr/include/c++/7/bits/shared_ptr.h:707

正确使用:

#include <iostream>
#include <memory>
using namespace std;

class A;
class B;


class A{
public:
    // 使用`weak_ptr` 代替 `shared_ptr`
    weak_ptr<B> b;
};


class B{
public:
    // 使用`weak_ptr` 代替 `shared_ptr`
    weak_ptr<A> a;
};


int main(int argc, char const *argv[])
{
    weak_ptr<A> wa;
    weak_ptr<B> wb;
    {
        shared_ptr<A> sa = make_shared<A>();
        shared_ptr<B> sb = make_shared<B>();

        // 循环引用
        sa->b = sb;
        sb->a = sa;

        wa = sa;
        wb = sb; 
    }

    cout << "sa: " << wa.use_count() << endl;
    cout << "sb: " << wb.use_count() << endl;

    return 0;
}

triangle@LEARN_FUCK:~$ make sa: 0 sb: 0

4. new/delete与智能指针

使用newdelete管理动态内存常出现的问题:

  • 忘记delete内存
  • 使用已经释放的对象
  • 同一块内存释放两次

智能指针能自动释放,就不存上述问题。

4. 野指针

  野指针不是NULL指针,是未初始化或者未清零的指针,在程序中乱指。

  • 指针变量没有被初始化
  • 指针指向的内存被释放了,但是指针没有置NULL
  • 指针越界

5. 指针与数组

5.1. 关系

类型 二次修改 参与运算 sizeof()
指针 可以 可以 指针的字节
数组 不行 可以 声明空间的字节

5.2. 数组

[!note|style:flat]

  • 当定义了char s[10];时,s的真实类型应该为char * const s;,所以s不可修改。
  • 在「形参」中,使用type name[] 与 type name[10]的形式接收数组,都会退化为type * name的形式,即name是当作指针处理了。

5.3. 数组稀奇古怪的初始化方式

方式 说明 注意
int a[10]; 局部变量:未初始化;
全局变量:初始化为0;
static修饰:初始化为0
int a[10] = {0} 肯定初始化为0。
int a[10] = {1,2,4} 前三个按照设置初始化,后面全是0
int a[10] = {1,2,4,} 编译通过 只能多写一个, 凸(▔皿▔╬)
int a[3] = {1,2,4,} 编译通过 凸(▔皿▔╬)
int a[3] = {1,2,3,4} 编译报错 {}写入的初始值个数不能大于数组声明值
const int N = 10;int n[N]; 编译通过 constN编译不通过
#define N 10; int n[N]; 编译通过

5.4. 字符数组长度的计算

char str[20] = "github";

// 计算字节数
sizeof(str);

// 计算字符串的长度
strlen(str);

[!note|style:flat] sizeof(类型)的计算值实在编译时,进行计算。以下计算是正确的。

int a[sizeof(int)];

results matching ""

    No results matching ""