C++ Idioms 02

  • 本文介绍了 C++ 的 5 个特别用法(Idioms),分别是
    • Concrete Data Type;
    • Lazy Evaluation;
    • Copy and Swap;
    • Copy on Write;
    • Reference Counting;

06. Concrete Data Type

用途

  • 通过允许或禁止动态内存分配来控制对象的生存期和作用域

示例代码

只允许通过动态内存分配的方式创建对象(方法一)

class EventHandler
{
public:
  virtual ~EventHandler() {}
};

class MouseEventHandler : public EventHandler
{
public:
  MouseEventHandler() {}
protected:
  ~MouseEventHandler() {}
};

int main() {
  MouseEventHandler mouse;  // ERROR!由于 MouseEventHandler 的析构函数是受保护的,因此对象
                            // 离开作用域时无法自动调用其析构函数,从而阻止了对象的自动创建
  EventHandler* event_handler = new MouseEventHandler(); // OK,动态创建的对象不调用析构函数
  delete event_handler;
  return 0;
}

只允许通过动态内存分配的方式创建对象(方法二)

class MouseEventHandler : public EventHandler
{
public:
  static MouseEventHandler* create()    // 必须是 static,因为创建对象之前没有现成对象可以调用成员函数,
    { return new MouseEventHandler(); } // 而对象的创建又被禁止
  void distroy() { delete this; }       // destroy() 应和 create() 成对使用
protected:
  MouseEventHandler() {}   // 构造函数和析构函数都是受保护的,因此对象无法自动创建
  ~MouseEventHandler() {}
};

只允许创建自动对象

class ScopeLock
{
private:
  static void* operator new (size_t size);              // Disallow new
  static void* operator new (size_t size, void* mem);   // Disallow placement new
};

07. Lazy Evaluation

用途

  • 等到真正要使用时才构造对象,尤其是非局部对象
  • 常用于 Singleton 模式

示例代码

问题:访问未初始化的非局部静态对象

class Bar
{
public:
  Bar() { cout << "Bar::Bar()" << endl; }
  void func() { cout << "Bar::func()" << endl; }
};

class Foo
{
public:
  Foo() { bar.func(); }
  static Bar bar;
};

Foo foo;      // 创建对象 foo 时调用了函数 Bar::func(),但此时还没有创建 Bar 的对象
Bar Foo::bar; 

int main() {
  return 0;  // output: Bar::func()
}            //         Bar::Bar() 

解决方法一:通过动态分配内存创建对象

class Foo
{
public:
  Foo() { bar().func(); }
  Bar& bar() {
    static Bar* b = new Bar();
    return *b;
  }
};

Foo foo;
//Bar Foo::bar;

int main() {  
  return 0;   // output: Bar::Bar()
}             //         Bar::func()  

解决方法二:创建自动对象

class Foo
{
public:
  Foo() { bar().func(); }
  Bar& bar() {
    static Bar b;
    return b;
  }
};

Foo foo;
//Bar Foo::bar;

int main() {  
  return 0;   // output: Bar::Bar()
}             //         Bar::func()  

08. Copy and Swap

用途

  • 实现一个异常安全的赋值运算符

示例代码

不进行自赋值检查

class String
{
  char* str;
public:
  String& operator=(const String& s) {
    String temp(s);
    temp.swap(*this);
    return *this;
  }

  void swap(String& s) throw() {
    std::swap(this->str, s.str);
  }
};

进行自赋值检查

class String
{
  char* str;
public:
  String& operator=(const String& s) {
    if (this != &s) String(s).swap(*this);
    return *this;
  }

  void swap(String& s) throw() {
    std::swap(this->str, s.str);
  }
};

09. Copy on Write

用途

  • 真正需要时才进行拷贝操作,否则只进行共享

示例代码

template <typename T>
class CowPtr
{
public:
  CowPtr(T* t) : m_sp(t) {}
  CowPtr(shared_ptr<T> t) : m_sp(t) {}
  CowPtr(const shared_ptr<T>& r) : m_sp(r) {}

  // 对常量指针解引用,不会改变内容,因此不必 detach
  const T& operator*() const { return *m_sp; }

  // 对普通指针解引用,可能需要改变内容,因此需要先令 m_sp 指向另一个副本,
  // 使得所有的修改都在这个副本进行,而不会影响到被其他指针占据的原生资源
  T& operator*() { detach(); return *m_sp; }
  
  // 分析同上
  const T* operator->() const { return m_sp.operator->(); }
  T* operator->() { detach(); return m_sp.operator->(); }

private:
  void detach() {
    T* tmp = m_sp.get();                     // 获取 m_sp 的原生指针,并将其另存一份至 tmp
    if (!(tmp == nullptr || m_sp.unique()))  // 若 m_sp 独占资源,则不必执行任何操作
      m_sp = shared_ptr<T>(new T(*tmp));     // 若 m_sp 与别的指针共享资源,则使其指向该资源的另一个副本
  }

private:
  shared_ptr<T> m_sp;
};

10. Reference Counting

用途

  • 引用计数,当多个指针或引用指向同一个对象时,应该由谁负责销毁操作

示例代码

class StringRep
{
  friend class String;
  friend ostream& operator<<(ostream& os, const StringRep& rep) {
    os << "[" << rep.data << ", " << rep.count << "]";
    return os;
  }

public:
  StringRep(const char* s) : count(1) {
    strcpy(data = new char[strlen(s) + 1], s);
  }

  ~StringRep() { delete[] data; }

private:
  size_t count;
  char* data;
};

class String
{
public:
  String() : rep(new StringRep("")) {
    cout << "default constructor: " << *rep << endl;
  }

  String(const String& s) : rep(s.rep) {
    rep->count++;
    cout << "copy constructor: " << *rep << endl;
  }

  String(const char* s) : rep(new StringRep(s)) {
    cout << "construct by const char*: " << *rep << endl;
  }

  String& operator=(const String& s) {
    cout << "before assign: " << *s.rep << " to " << *rep << endl;
    String(s).swap(*this);
    cout << "after assign: " << *s.rep << ", " << *rep << endl;
    return *this;
  }

  ~String() {
    if (rep && rep->count <= 1) {
      cout << "destructor: " << *rep << endl;
      delete rep;
    }
  }

private:
  void swap(String& s) throw() { std::swap(rep, s.rep); }

private:
  StringRep* rep;
};

int main() {
  String str1;
  {
  str1 = "ABC";
  String str2(str1);
  }
  str1 = "CBA";
  return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值