首页 > 其他分享 >对象切片

对象切片

时间:2022-09-25 11:12:13浏览次数:46  
标签:对象 切片 shape new circle type ptr

C++支持将对象储存在栈上,但很多情况,对象不能,或不应该存储在栈上。比如:

  • 对象很大
  • 对象的大小在编译时不能确定
  • 对象是函数的返回值,但由于特殊的原因,不应使用对象的值返回

常见情况之一是,在工厂方法或其他面向对象编程的情况下,返回值类型是基类(的指针或引用)
举例:是对工厂方法的简单演示:

enum class shape_type {
  circle,
  triangle,
  rectangle,
  …
};

class shape { … };
class circle : public shape { … };
class triangle : public shape { … };
class rectangle : public shape { … };

shape* create_shape(shape_type type)
{
  …
  switch (type) {
  case shape_type::circle:
    return new circle(…);
  case shape_type::triangle:
    return new triangle(…);
  case shape_type::rectangle:
    return new rectangle(…);
  …
  }
}

这个create_shape方法会返回一个shape对象,对象的实际类型是某个shape的子类,圆/三角形等。在这种情况下,函数的返回值只能是指针或其变体形式。如果返回类型是shape,实际却返回一个circle,编译器不会报错,但结果多半是错的,这种现象就叫做对象切片(object slicing),是C++特有的一种编码错误。这种错误不是语法错误,而是一个对象复制相关的语义错误,也算是C++的一个陷阱,所以要注意这种情况。

那怎样才能确保在使用create_shape的返回值时不发生内存泄漏呢?
答案就在析构函数和它的栈展开行为上。 只需要把这个返回值存放在一个本地变量里,并确保其析构函数会删除该对象即可。
简单的实现如下:

class shape_wrapper {
public:
  explicit shape_wrapper(
    shape* ptr = nullptr)
    : ptr_(ptr) {}
  ~shape_wrapper()
  {
    delete ptr_;
  }
  shape* get() const { return ptr_; }
private:
  shape* ptr_;
};

void foo()
{
  …
  shape_wrapper ptr_wrapper(
    create_shape(…));
  …
}

delete空指针会发生什么?答案就是这是一个合法的空操作。

在new一个对象和delete一个指针时编译器需要干不少的活,大致翻译如下:

// new circle(…)
{
  void* temp = operator new(sizeof(circle));
  try {
    circle* ptr =
      static_cast<circle*>(temp);
    ptr->circle(…);
    return ptr;
  }
  catch (...) {
    operator delete(ptr);
    throw;
  }
}

new的时候会先分配内存(失败时整个操作失败并向外抛出异常,通常是bad_alloc),然后在这个结果指针上构造对象;构造成功后则new操作整体完成,否则释放刚分配的内存并继续向外抛构造函数产生的异常。

if (ptr != nullptr) {
  ptr->~shape();
  operator delete(ptr);
}

delete时先判断指针是否为空,在指针不为空时调用析构函数并释放之前分配的内存。

回到 shape_wrapper 和它的析构行为。在析构函数里做必要的清理工作,这就是 RAII 的基本用法。这种清理并不限于释放内存,也可以是:

  • 关闭文件(fstream的析构就会这么做)
  • 释放同步锁
  • 释放其他重要的系统资源

例如,我们应该使用:

std::mutex mtx;

void some_func()
{
  std::lock_guard<std::mutex> guard(mtx);
  // 做需要同步的工作
}

而不是:

std::mutex mtx;

void some_func()
{
  mtx.lock();
  // 做需要同步的工作……
  // 如果发生异常或提前返回,
  // 下面这句不会自动执行。
  mtx.unlock();
}

标签:对象,切片,shape,new,circle,type,ptr
From: https://www.cnblogs.com/whiteBear/p/16727436.html

相关文章

  • 购物车程序的面向对象设计(一)
    一、人员分工1.网络2113202121333063徐彬晶负责商品类、商城类、仓库类、菜单类(普通用户菜单)、参与讨论博客的编写2.网络2113202121333064黄坤负责商品类、购物车......
  • 13.8 __new__与__init__演示创建对象的过程
     '''__new__用于创建对象,__init__把创建的对象初始化'''classPerson(object):def__new__(cls,*args,**kwargs):print('__new__被调用执行了,cls的......
  • 前天为群友写的,使用面向对象的写法封装一个Modal Dialogue Box
    q群里的群友提出的问题“如何使用面向对象的语法封装一个Modal框”我们都知道,现在使用vue,都是用组件去封装的,怎么用对象去封装呢?我比较感兴趣,做了以下尝试下面直接......
  • 大对象和finalzier对象创建相比小对象会更慢
    《Pro.NETMemoryManagement》Chapter6P428中讲道大对象(包括Obj对象头部等,总字节占85000及以上)和有finalizer的对象生成的构造函数(JIT_New)会更为复杂,因此开销会更......
  • C++自学笔记 面向对象程序设计OOP(Object Oriented Programming)
    什么是对象?Objects=Attributes+ServicesData:ThepropertiesorstatusOperations:thefuctionsC语言中定义的变量用来存储数据,所以它可以理解为是“对象”“......
  • JavaScript Window - 浏览器对象模型(BOM)
     浏览器对象模型(BOM):现代浏览器已经实现了JavaScript交互性的相同方法和属性,所以常被认为是BOM的方法和属性Window对象:所有浏览器都支持window对象,表示浏览器的窗......
  • JavaScript Boolean(布尔) 对象
    Boolean对象:用于转换一个不是Boolean类型的值转换为Boolean类型值(true或者false).Boolean对象属性:constructor:返回对创建此对象的Boolean函数的引用prototype......
  • JavaScript Array(数组) 对象
    数组:数组对象是使用单独的变量名来存储一系列的值数组可以用一个变量名存储所有的值,并可以用变量名访问任何一个值数组中每一个元素都有自己的id,方便访问创建数组:三种......
  • JavaScript 字符串(String) 对象
    字符串可以使用单引号或者双引号使用位置索引可以访问字符串中的任何字符,字符第一个字符为【0】,依次等可在字符串中使用引号varanswer="Heiscalled'Johnny'";也可......
  • JavaScript Number 对象
    JavaScript数字:可以使用也可以不使用小数点来书写极大或极小的数字可通过科学(指数)计数法来写 JavaScript数字均为64位:所有的数字都是由浮点型类型  精度:......