1 Star 0 Fork 1.4K

xgdu / OpenArkCompiler

Create your Gitee Account
Explore and code with more than 12 million developers,Free private repositories !:)
Sign up
This repository doesn't specify license. Please pay attention to the specific project description and its upstream code dependency when using it.
Clone or Download
CPPCodingTalkAboutPointer.md 5.93 KB
Copy Edit Raw Blame History

C++编程探讨之指针

背景

C/C++中指针的使用具有极大的灵活性,伴随着的是更多的安全风险,同时这也对程序员提出了更高的要求。本文将讨论裸指针在C/C++中当如何被使用,乃至最终确立一种编码范式。

裸指针vs引用

成员访问

当访问对象成员时,裸指针存在为空的场景(指针的有效性由闭合对象或函数从逻辑上自保证),所以必须检查非法指针。而引用必定非空。

做容器成员

引用从C++语义中,表达的是别名关系,理论上不占内存(实际中规中矩的编译器对于引用的内部实现是指针)。引用本身不是对象,这点与指针不同,指针可以作为各容器成员,而引用不行。

裸指针vs智能指针

堆对象销毁

class Int {
  ...
 private:
  int data;
}

void test(int* in) {
  Int* tmp = new Int();
  ...
  goto LABEL;
  ...

  delete tmp;
LABEL:
}

对于资源(堆对象、栈对象、文件资源等)的使用,遵循**“谁申请,谁释放”**的原则(RAII),这样可以最大限度的降低资源泄露的可能。

裸指针的newdelete之间往往会包含一段处理逻辑以及子函数调用,中间的处理逻辑可能发生异常、跳转等动作(中间的处理逻辑的行为不会由当前对象越权限制,超出new行为的管辖范围),而跳过资源的释放,从而造成资源泄露(如示例中test函数中tmp对象)。

智能指针改造为auto tmp = std::make_unique<Int>();,构造对象tmp时,即绑定其delete行为,退出当前作用域销毁,而避免了资源泄露的可能。

管理权vs使用权

int* delete(int* in);

管理权:拥有销毁、重建对象的权利

使用权:拥有访问、修改对象的权利

如上示例所示,当使用裸指针传递参数时,由于其隐含了转移所有权的属性(可能转移所有权,亦可能没有),入参in以及出参均无法确定行使了管理权还是使用权。调用此函数将需要额外补充信息:in是否会被delete函数销毁?返回值是否需要调用者销毁?

std::unique_ptr<int> delete(std::unique_ptr<int>& in);

使用智能指针将在接口中明确表达参数的角色,如std::unique_ptr<int>& in代表delete函数享有其使用权,函数返回值代表delete函数转移所有权。

指针使用范式

new创建的对象,必须立即绑定其销毁方式

错误示例:

Object* obj = new Object();
...
delete obj;

正确示例:

std::unique_ptr<Object> obj = std::make_unique(new Object());

申请的资源,必须立即绑定其释放方式

错误示例:

FILE* file = open("xxx.txt");
...
file->close();

正确示例(本例比较通用,最佳方式应用类封装open):

template <typename T, typename Func>
class ResourceGuard {
 public:
  ResourceGuard(T* _obj, Func _func) : obj(_obj), func(_func) {}

  ~ResourceGuard() { obj.func(); }
 private:
  T* obj;
  Func func;
}

FILE* file = open("xxx.txt");
auto fileGuard = ResourceGuard<FILE, std::function<void()>>(file, FILE::close);
...

确定不为空的场景,使用引用而非指针

错误示例:

void func1(int* in) {
  if (in == nullptr) return;
  ...
}

void func2() {
  int* p = nullptr;
  ...
  if (p != nullptr) {
    func1(p);
  }
}

正确示例:

void func1(int& in) {
  ...
}

void func2() {
  int* p = nullptr;
  ...
  if (p != nullptr) {
    func1(*p);
  }
}

作为容器成员(不具管理权),确定不为空时,使用封装的引用容器,而非指针

错误示例:

void func(std::vector<int *>& in) {
  for (auto *p : in) {
    if (p == nullptr) {
      continue;
    }
    ...
  }
}

正确示例:

template <typename T>
class Ref {
 public:
  Ref() = delete;
  Ref(T& ref) : data(&ref) {}

  ...

  operator T() const noexcept {
    return *data;
  }

 private:
  T* data;
}

template <typename T>
using ref_vector = std::vector<Ref<T>>;
void func(ref_vector<int>& in) {
  for (auto p : in) {
    int& data = p;
    ...
  }
}

作为容器成员(具备管理权),使用具有管理生命周期的容器,而非指针容器

错误示例:

std::vector<int *> data;
...
for (auto *p : data) {
  delete p;
}

正确示例:

template <typename T>
class ptr_vector {
 public:
  ~ptr_vector() {
    for (auto *p : data) {
      delete p;
    }
  }

 private:
  std::vector<T *> data;
}

ptr_vector<int> data;
...

显示转移对象管理权,明确对象使用权

C++11新增了move语义,并废弃auto_ptr而使用需显示转移所有权的unique_ptr,使得栈对象和堆对象的生命周期管理方式可以进行统一。

栈对象转移示例:

std::vector<int> func() {
  std::vector<int> data;
  data.push_back(0);
  return std::move(data);
}

模糊的堆对象转移示例:

Object* func() {
  std::unique_ptr<Object> data = std::make_unique(new Object);
  Object& rData = ToRef(data);
  rData.push_back(0);
  return data.release();
}

明晰的的堆对象转移示例:

std::unique_ptr<Object> func() {
  std::unique_ptr<Object> data = std::make_unique(new Object);
  Object& rData = ToRef(data);
  rData.push_back(0);
  return std::move(data);
}

应当使用指针场景

  1. 第三方库函数传入或传出指针,但必须在调用前一刻使用unique_ptr.get()unique_ptr.release()构建入参,出参也必须在拿到后立即使用unique_ptr接住或判空并转引用。
  2. 作为容器成员(不具管理权),使用场景中有空指针设计,但必须在使用前立即判空并转引用,不支持指针扩散。

备注

上述的Refref_vector已开发完成,Ref由于operator.无法被重载,所以定义为SafePtr

上述的ResourceGuardptr_vector正在开发中,文中主要为示意。

C++
1
https://gitee.com/xgdu/OpenArkCompiler.git
git@gitee.com:xgdu/OpenArkCompiler.git
xgdu
OpenArkCompiler
OpenArkCompiler
master

Search