- 本文介绍了 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;
EventHandler* event_handler = new MouseEventHandler();
delete event_handler;
return 0;
}
只允许通过动态内存分配的方式创建对象(方法二)
class MouseEventHandler : public EventHandler
{
public:
static MouseEventHandler* create()
{ return new MouseEventHandler(); }
void distroy() { delete this; }
protected:
MouseEventHandler() {}
~MouseEventHandler() {}
};
只允许创建自动对象
class ScopeLock
{
private:
static void* operator new (size_t size);
static void* operator new (size_t size, void* mem);
};
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;
Bar Foo::bar;
int main() {
return 0;
}
解决方法一:通过动态分配内存创建对象
class Foo
{
public:
Foo() { bar().func(); }
Bar& bar() {
static Bar* b = new Bar();
return *b;
}
};
Foo foo;
int main() {
return 0;
}
解决方法二:创建自动对象
class Foo
{
public:
Foo() { bar().func(); }
Bar& bar() {
static Bar b;
return b;
}
};
Foo foo;
int main() {
return 0;
}
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) {}
const T& operator*() const { return *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();
if (!(tmp == nullptr || m_sp.unique()))
m_sp = shared_ptr<T>(new T(*tmp));
}
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;
}