类(二):深入理解

类的析构函数

析构函数

析构函数用于销毁不再需要或超出其作用域的对象。对象超出其作用域时,程序将自动调用类的析构函数。

销毁对象需要释放该对象的数据成员(静态成员除外)占用的内存。

析构函数名= ~ + 类名 。

析构函数有特定的名称,没有任何形参,所以一个类只有一个析构函数。

给析构函数指定返回值或形参是错误的。

默认的析构函数

如果没有定义自己的析构函数,编译器会自动生成默认的析构函数。

默认的析构函数不能删除在空闲存储器中分配的对象或对象成员。

如果类的成员占用的空间是在构造函数中动态分配的,就必须自定义析构函数,然后释放以前分配的内存,就像销毁普通变量一样。

析构函数与动态内存分配

可以在构造函数中使用new操作符来为对象成员分配空间。在这种情况下,必须提供适当的析构函数,在不在需要该对象时释放内存(或使用智能指针)。

 

 delete操作符删除动态创建的对象时,delete操作符将释放该对象占用的内存之前,首先调用该对象的析构函数,这可以确保释放为类的成员动态分配的任何内存


实现复制构造函数

 解决方案是提供一个复制构造函数来代替默认版本。(完全复制对象的动态分配的成员)

 

将形参指定为const引用,避免对复制构造函数的无穷调用。

如果动态地为类的成员分配空间,则必须实现复制构造函数。


运算符重载

运算符重载功能不允许使用新的运算符,也你不允许改变运算符的优先级。

不能重载的运算操作符

实现重载的运算符

bool operator<(const CBox& box1) const; //重载小于运算符

const表示该函数不修改本类的数据成员。

 

 成员运算符函数总是以左边的实参作为this指针。

如果要编写如new、delete运算符函数,必须在关键字operator和运算符名称之前加个空格。

重载运算符可用于const和非const对象。

实现对比较运算符的完全支持

下图形式只能用普通函数或友元函数处理。

 

 

 

 

重载赋值运算符

如果不提供重载的赋值运算符函数,则编译器提供一个默认的函数。

默认版本只提供逐个成员的复制过程,与默认赋值构造函数的功能类似,但是不要混淆默认复制构造函数与默认赋值运算符

当定义以现有的同类对象进行初始化的类对象,或者通过以传值方式给函数传递对象时,调用默认复制构造函数。

当赋值语句的左边和右边是同类类型的对象时,调用默认赋值运算符。

 

  C++对赋值运算符的形参和返回类型没有任何限制。

重载递增递减运算符

++和--有前缀和后缀两种形式。

区分重载运算符前缀和后缀形式的首要方法是利用参数列表。前缀形式没有形参后缀形式有形参

 

 

重载函数调用运算符

函数调用操作符是(),此操作符的函数重载是operator()()。

重载函数调用操作符的类对象,称为函数对象或仿函数(functor)。


复制对象问题

 按值传递实参到函数时,复制是隐式的。如果实参时类对象,系统开销会很大。rvalve引用实参是解决这这个问题的关键。

避免不必要的复制操作

应用rvalue引用形参

 

 对于定义复杂对象的类,实现移动赋值运算符、移动构造函数、赋值运算符和复制构造函数,会大大地提高性能。

命名的对象是lvalue

如果是一个命名的变量,则它是一个lvalue,这可能会重新导致效率低下。


默认的类成员

 

使用default关键字可以指定包含默认提供的成员。

  


类模板

类模板本身不是类,只是编译器用来生成类代码的一种方法。

 类模板的主要用途是定义库类,尤其是组织和存储对象集合的容器类。一般不会给应用程序定义模板。

定义类模板

关键字template表示正在定义的是模板,关键字template在关键字class和类名之前,在尖括号内的类型形参T之前。可以用typename代替class来指定形参。

模板函数成员

 可以将类模板成员函数的定义放在模板定义的外部。此时,其定义必须放在包含模板定义的头文件中,而不是单独的cpp文件中。

 

函数名前用模板名后跟尖括号中的形参T来限定。
模板名和形参的组合可以识别出函数模板的实例属于哪个类。

 

根据类模板创建对象

函数模板的类型形参是使用特定的函数隐式确定的。

以类模板为基础创建对象,必须在声明中指定类名后面的类型形参。

 

 有多个形参的类模板

函数对象模板

 


完美转发

是指在函数模板中,完全依照模板的参数类型,将参数传递给函数模板中调用的另外一个函数。

完美转发仅与类模板或函数模板的环境相关。

假设有一个函数fun1(),它用一个类类型T来参数化。

另一个函数fun2()有2个版本,一个版本有一个lvalue引用形参,另一个版本有一个rlvaue引用形参。

fun1()把它接收的实参传送给fun2(),当fun1()收到的实参为rvalue引用时,调用rvalue引用形参的fun2()。

当fun1()收到的实参为lvalue或lvalue引用时,调用lvalue引用形参的fun2()。

 


模板形参的默认实参

函数模板的默认实参

在模板定义中,有默认值的模板形参必须放在没有默认值的形参后面。为形参提供实参时,列表中所有前面的形参都必须指定实参。

类模板的默认实参

 类模板的任何形参或所有形参都可以有默认实参。

定义默认形参值得规则是相同的:所有指定默认值得形参都必须放在形参列表的尾部。

如果创建模板的实例时为任意形参提供了值,就必须指定列表中所有前面的形参值。

 


类模板的别名

对于指定了一些或全部形参值的类模板,可以给它定义别名。

使用using关键字。

 


模板特例

函数或类模板中的一些形参值可能是无效的。

指针类型通常不能用于模板,除非为模板提供了一个特殊的规范——模板特例,模板特例是为一组特殊的形参值制定的一个额外的模板定义。

可以定义一个函数重载,代替模板特例。


组织程序代码

 box.h

#pragma once
#include <utility>
using std::rel_ops::operator<=;
using std::rel_ops::operator>;
using std::rel_ops::operator>=;
using std::rel_ops::operator!=;

class CBox
{
public:
  explicit CBox(double lv = 1.0, double wv = 1.0, double hv = 1.0);
  ~CBox();
private:
  double m_Length;
  double m_Width;
  double m_Height;
public:
  // Less-than operator for CBox objects
  bool operator<(const CBox& aBox) const
  {
    return volume() < aBox.volume();
  }

  // Operator function for == comparing CBox objects
  bool operator==(const CBox& aBox) const
  {
    return volume() == aBox.volume();
  }

  // Calculate the box volume
  double volume() const
  {
    return m_Length*m_Width*m_Height;
  }

  double getLength() const { return m_Length; }
  double getWidth() const { return m_Width; }
  double getHeight() const { return m_Height; }

  CBox operator+(const CBox& aBox) const;     // Addition operator for CBox objects
  CBox operator*(int n) const;                // Multiply operator for CBox objects
  int operator/(const CBox& aBox) const;      // Division operator for CBox objects
};

box.cpp

#include "Box.h"
#include <algorithm>


CBox::CBox(double lv, double wv, double hv) :
m_Length{ std::max(lv, wv) }, m_Width{ std::min(lv, wv) }, m_Height{ hv }
{
  if (lv < 0.0 || wv < 0.0 || hv < 0.0)
    throw "Negative dimension specified for CBox object.";

  // Ensure the height is <= width is <= length
  if (m_Height > m_Length)
  { // height greater than length, so swap them
    std::swap(m_Height, m_Length);
  }
  else if (m_Height > m_Width)
  { // height less than or equal to length but greater than width so swap
    std::swap(m_Height, m_Width);
  }
}


CBox::~CBox()
{
}

// Addition operator for CBox objects
CBox CBox::operator+(const CBox& aBox) const
{
  // New object has larger length and width and sum of the heights
  return CBox{ std::max(m_Length, aBox.m_Length), std::max(m_Width, aBox.m_Width), m_Height + aBox.m_Height };
}

// CBox multiply operator this*n
CBox CBox::operator*(int n) const
{
  if (n % 2)
    return CBox{ m_Length, m_Width, n*m_Height };             // n odd
  else
    return CBox{ m_Length, 2.0*m_Width, (n / 2)*m_Height };   // n even
}

// Division operator - divide one Cbox object by another
int CBox::operator/(const CBox& aBox) const
{
  // Number of boxes in horizontal plane this way
  int tc1{ static_cast<int>((m_Length / aBox.m_Length))*
    static_cast<int>((m_Width / aBox.m_Width)) };
  // Number of boxes in horizontal plane that way
  int tc2{ static_cast<int>((m_Length / aBox.m_Width))*
    static_cast<int>((m_Width / aBox.m_Length)) };

  //Return best fit
  return static_cast<int>((m_Height / aBox.m_Height)*(tc1 > tc2 ? tc1 : tc2));
}

// Operator to return the free volume in a packed box
inline double operator%(const CBox& aBox, const CBox& bBox)
{
  return aBox.volume() - ((aBox / bBox)*bBox.volume());
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值