`

Effective c++条款19:设计class犹如设计type

 
阅读更多

Treat class design as type design

条款19:Treat class design as type design(设计class犹如设计type)。
C++和其它OOP语言一样,当你定义一个class时就等于定义了一个新type,所以对于一个新type应该带着和设计内置类型一样谨慎的态度去研讨设计。下面是需要注意的几点(对于每一点我都编写了相关的示例代码):
1、新type的对象应该如何被创建和销毁?
即是否需要定义自己的operator new、operator new[]、operator delete和operator delete[]。看一段实现自定义operator new的代码:
#include "stdafx.h"
#include <stdlib.h>
#include <string>
using namespace std;

class String {
public:
String(const char *buffer);
~String();
void* operator new(size_t size);
private:
char *_buffer;
};

String::String(const char *buffer) {
if (buffer == NULL)
return;
size_t len = strlen(buffer);
_buffer = new char[len];
strcpy(_buffer, buffer);
}

String::~String() {
if (_buffer != NULL)
delete[] _buffer;
}

void* String::operator new(size_t size) {
return ::operator new(size);
}

int _tmain(int argc, _TCHAR* argv[]) {
String str("string");
system("pause");
return 0;
}

#include "stdafx.h"
#include <stdlib.h>
#include <string>
using namespace std;

class String {
public:
String(const char *buffer);
~String();
void* operator new(size_t size);
private:
char *_buffer;
};

String::String(const char *buffer) {
if (buffer == NULL)
return;
size_t len = strlen(buffer);
_buffer = new char[len];
strcpy(_buffer, buffer);
}

String::~String() {
if (_buffer != NULL)
delete[] _buffer;
}

void* String::operator new(size_t size) {
return ::operator new(size);
}

int _tmain(int argc, _TCHAR* argv[]) {
String str("string");
system("pause");
return 0;
}
2、明确对象的初始化和对象的赋值操作有什么区别。看下面的代码:
class String {
public:
String(const char* buffer) : _buffer(0) {
_buffer = const_cast<char*>(buffer);
}
~String() {
if (_buffer != NULL)
delete[] _buffer;
}
private:
char *_buffer;
};
3、新type的对象如何被pass by value,意味着什么?通常来说copy构造函数完成pass by value。看下面的代码:
class String {
public:
String(const char* buffer) : _buffer(0) {
_buffer = const_cast<char*>(buffer);
}
String(const String& rhs) {
size_t len = rhs.Length();
_buffer = new char[len];
strcpy(_buffer, rhs.CStr());
}
~String() {
if (_buffer != NULL)
delete[] _buffer;
}
size_t Length(void) const { return strlen(_buffer); }
const char* CStr(void) const { return _buffer; }
private:
char *_buffer;
};
4、什么是新type的合法值?也就是确保成员变量调用所谓的访问函数时提供的合法性。看下面的代码(通过size_t类型的约束至少可以确保分配的大小不会申请是负数,因为这样毫无意义):
class String {
public:
String(const char* buffer) : _buffer(0) {
_buffer = const_cast<char*>(buffer);
}
String(const String& rhs) {
size_t len = rhs.Length();
_buffer = new char[len];
strcpy(_buffer, rhs.CStr());
}
~String() {
if (_buffer != NULL)
delete[] _buffer;
}
void Realloc(size_t newSize) {
const char *oldBuffer = _buffer;
if (oldBuffer != NULL) {
_buffer = new char[newSize];
strcpy(_buffer, oldBuffer);
delete[] oldBuffer;
} else
_buffer = new char[newSize];
}
size_t Length(void) const { return strlen(_buffer); }
const char* CStr(void) const { return _buffer; }
private:
char *_buffer;
};
5、你的新type需要配合某个继承体系吗?看下面代码(如果确定某个类型为基类型,那一定要注意它的析构函数必需是virtual,以确保不会出现不必要的内存泄露):
class Windows {
public:
Windows() { }
virtual ~Windows() { }
};

class WindowsXP : private Windows {

};
6、你的新type需要什么样的转换?某些情况下希望可以隐式转换为另一个类型。看下面的代码:
#include "stdafx.h"
#include <stdlib.h>
#include <string>
using namespace std;

class Int32 {
public:
Int32(int value)
: _value(value) { }
~Int32() { }
operator int() {
return _value;
}
private:
int _value;
};

int _tmain(int argc, _TCHAR* argv[]) {
int i = Int32(5);
system("pause");
return 0;
}
7、什么样的操作符和函数对此新type而言是合理的?即某些情况是否应该使其成为友联或者不具备关系。
8、什么样的标准函数应该驳回?如果不需要编译生成的函数那就明显拒绝它。
class noncopyable {
protected:
noncopyable() { }
~noncopyable() { }
private:
noncopyable(const noncopyable&);
noncopyable& operator=(const noncopyable&);
};
9、谁该取用新type的成员?即合理的设计哪些成员是private、protected、public、friend。
10、什么是新type的未声明接口?一般来说指那些在新type内部工作的接口或类型。它对效率、异常安全性及资源运用提供保证。
11、你的新type有多么一般化?或许你定义的并不是一个type,而是定义了一个整个types家族。这个时候需要考虑使用template。比如一个通用的可变数组容器,看下面代码的简单实现:
template<typename T>
class Array {
Array() : Array(4) { }
Array(size_t capacity);
~Array() {
delete[] _arr;
}
size_t Size(void) const { return _size; }
size_t Capacity(void) const { return _capacity; }
void Add(T data);
private:
void Realloc(size_t newSize);
private:
T *_arr;
size_t _capacity;
size_t _size;
};

template<typename T>
Array<T>::Array(size_t capacity) {
*_arr = new T[capacity];
_capacity = capacity;
_size = 0;
}

template<typename T>
void Array<T>::Add(T data) {
if (_size >= _capacity)
Realloc(_capacity << 1);
*_arr[_size++] = data;
}

template<typename T>
void Array<T>::Realloc(size_t newSize) {
T *old = _arr;
_arr = new T[newSize];
_capacity = newSize;
memcpy(_arr, old, _capacity);
delete[] old;
}
11、你真的需要一个新type吗?如果只是定义一个新的派生类型以便添加一两个简单的函数,可以考虑是否template化或者在基类型中添加。

参考:Effective C++ Third Edition(Scott Meyers著)P.83 – P.86

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics