This action will force synchronization from 方舟编译器/OpenArkCompiler, which will overwrite any changes that you have made since you forked the repository, and can not be recovered!!!
Synchronous operation will process in the background and will refresh the page when finishing processing. Please be patient.
C/C++
中指针的使用具有极大的灵活性,伴随着的是更多的安全风险,同时这也对程序员提出了更高的要求。本文将讨论裸指针在C/C++
中当如何被使用,乃至最终确立一种编码范式。
当访问对象成员时,裸指针存在为空的场景(指针的有效性由闭合对象或函数从逻辑上自保证),所以必须检查非法指针。而引用必定非空。
引用从C++
语义中,表达的是别名关系,理论上不占内存(实际中规中矩的编译器对于引用的内部实现是指针)。引用本身不是对象,这点与指针不同,指针可以作为各容器成员,而引用不行。
class Int {
...
private:
int data;
}
void test(int* in) {
Int* tmp = new Int();
...
goto LABEL;
...
delete tmp;
LABEL:
}
对于资源(堆对象、栈对象、文件资源等)的使用,遵循**“谁申请,谁释放”**的原则(RAII),这样可以最大限度的降低资源泄露的可能。
裸指针的new
与delete
之间往往会包含一段处理逻辑以及子函数调用,中间的处理逻辑可能发生异常、跳转等动作(中间的处理逻辑的行为不会由当前对象越权限制,超出new
行为的管辖范围),而跳过资源的释放,从而造成资源泄露(如示例中test
函数中tmp
对象)。
智能指针改造为auto tmp = std::make_unique<Int>();
,构造对象tmp
时,即绑定其delete
行为,退出当前作用域销毁,而避免了资源泄露的可能。
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);
}
unique_ptr.get()
或unique_ptr.release()
构建入参,出参也必须在拿到后立即使用unique_ptr
接住或判空并转引用。上述的Ref
、ref_vector
已开发完成,Ref
由于operator.
无法被重载,所以定义为SafePtr
。
上述的ResourceGuard
、ptr_vector
正在开发中,文中主要为示意。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。