- 索引
- 26、将构造函数和非成员函数虚拟化
- 27、限制某个类所能产生的对象数量
- 1、建立对象的环境
- 2、一个具有对象计数功能的基类
- 28、要求或禁止在堆中产生对象
- 1、使用重载operator new观察该对象是否在堆中创建
- 2、根据堆栈的位置判断是否位于堆
- 3、判断是否能删除一个指针
- 4、禁止堆对象
- 29、灵巧指针
- 1、samrt指针转换为dunb指针
- 2、Smart和继承类与基类的类型转换
- 3、Smart指针与const
- 30、引用计数
- 1、如何共享相同值
- 2、防止通过指针接收非const []的返回值引用
- 3、带引用计数的基类
- 4、自动的引用计数处理:省去`addReference`和`removeReference`
- 5、在现存类上增加引用计数
- 31、代理类
- 1、实现二维数组
- 2、区分通过operator[]进行的是读 *** 作还是写 *** 作
- 4、总述
- 32、让函数根据一个以上的对象来决定怎么虚拟
- 1、用虚函数加RTTI
- 2、只用虚函数
- 3、模拟虚函数表
- 4、初始化模拟虚函数表
- 4、使用非成员函数
C++ | 【01 基础提议】More Effective C++🧡💛💚💙
C++ | 【02 运算符】More Effective C++🧡💛💚💙
C++ | 【03 异常】More Effective C++🧡💛💚💙
C++ | 【04 效率】More Effective C++🧡💛💚💙
C++ | 【05 技巧】More Effective C++🧡💛💚💙
C++ | 【06 杂项】More Effective C++🧡💛💚💙
虚拟构造函数:行为和构造相似,根据输入的数据的不同而建立不同类型的对象;
// 基类
class NLComponent {
public:
virtual void func() = 0;
};
// 派生类
class TextBlock : public NLComponent {
};
// 派生类
class Graphic : public NLComponent {
};
class NewsLetter {
public:
NewsLetter(istream& str);
private:
static NLComponent* readComponent(istream& str);
list<NLComponent*> m_components;
};
// 从string中读取component对象
NewsLetter::NewsLetter(istream& str) {
while(str) {
m_components.push_back(readComponent(str));
}
}
虚拟拷贝构造函数:返回一个指针,指向调用该函数的对象的新拷贝;
class NLComponent {
public:
virtual NLComponent* clone() const = 0;
};
// 派生类
class TextBlock : public NLComponent {
public:
virtual TextBlock* clone() const {
return new TextBlock(*this);
}
};
// 派生类
class Graphic : public NLComponent {
virtual Graphic* clone() const {
return new Graphic(*this);
}
};
class NewsLetter {
public:
NewsLetter(const NewsLetter& rhs) {
for(auto i:rhs.m_components) {
m_components.push_back((*i).clone());
}
}
private:
list<NLComponent*> m_components;
};
非成员函数虚拟化
让内联非成员函数调用虚函数;
inline NLComponent* clone(const NLComponent& c) {
return c.clone();
}
27、限制某个类所能产生的对象数量
很简单,如果想要阻止某个类对象被创建,只需将其构造函数声明在private内;
通过设置num来限制创建对象的数量;
class Printer{
public:
void TooManyOj() {};
Printer(){
if(numOj >= 1) throw TooManyOj();
numOj++;
}
~Printer() {
--numOj;
}
private:
static size_t numOj;
Printer(const Printer& rhs);
};
1、建立对象的环境
带有private构造函数的类不能作为基类使用,也不能嵌入到其他对象中;
使用伪构造函数【允许建立任意num的对象,但想要禁止从FSA派生出新类】:创建对象后再通过auot_ptr进行管理;
// 有限态自动机
class FSA{
public:
FSA* makeFSA();
FSA& makeFSA(const FSA& rhs);
priavte:
FSA();
~FSA(const FSA& rhs);
};
FSA* FSA::makeFSA() {
return new FSA();
}
FSA& FSA::makeFSA(const FSA& rhs) {
return new FSA(rhs);
}
2、一个具有对象计数功能的基类
这里使用private继承,故使用using恢复访问权,且若使用public继承,则需要给Counted一个虚拟
析构函数,以防对象被删除;
template<class BeingCounted>
class Counted {
public:
class ToomanyOj{};
static int ojCnt() { return numOj;}
protected:
Counted();
Counted(const Counted& rhs);
~Counted();
private:
static int numOj;
static const size_t maxOj=10;
void init();
};
template<class BeingCounted>
Counted<BeingCounted>::Counted() { init(); }
template<class BeingCounted>
Counted<BeingCounted>::Counted(const Counted<BeingCounted>& rhs) { init(); }
template<class BeingCounted>
void Counted<BeingCounted>::init() {
if(numOj >= maxOj) throw ToomanyOj();
++numOj;
}
template<class BeingCounted>
int Counted<BeingCounted>::numOj = 0;
class Printer : private Counted<Printer> {
public:
static Printer* makePrinter();
static Printer* makePrinter(const Printer& rhs);
~Printer();
void submitJob(const PrintJob& job);
void reset();
using Counted<Printer>::ojCnt;
using Counted<Printer>::ToomanyOj;
private:
Printer();
Printer(const Printer& rhs);
};
28、要求或禁止在堆中产生对象
非堆对象:在定义时自动构造,结束时自动释放;故只需禁止使用隐式构造和析构即可;
即将析构为private,而采用伪析构函数来访问析构函数;
【异常处理体系中要求栈中对象的析构必须伪public】
该类型的类禁止继承和复合;若需要则将其修改为protected
class UPNumber {
public:
UPNumber();
void destroy() const { delete this; }
private:
~UPNumber();
};
int main() {
UPNumber pp;
UPNumber *p = new UPNumber;
delete p;
p->destroy();
return 0;
}
1、使用重载operator new观察该对象是否在堆中创建
通过加入一个标记,来判断是否在堆中;
- 当使用数组分配时,虽然可以重载[],但第一次回被置为true,下一次即会抛出异常;
- 若使用 UPNumber* p = new UPNumber(*new UPNumber);也可能导致相同错误,由于C++没有保证调用顺序
class UPNumber {
public:
UPNumber() {
if(!onHeap){
throw ;
}
onHeap = false;
}
static void* operator new(size_t size);
private:
static bool onHeap;
};
bool UPNumber::onHeap = false;
void* UPNumber::operator new(size_t size) {
onHeap = true;
return ::operator new(size);
}
int main() {
UPNumber* p = new UPNumber[10];
return 0;
}
2、根据堆栈的位置判断是否位于堆
由于堆是底部向上,而栈是顶部往下故判断两个地址的大小来确定是否位于堆;
但静态变量于堆下部,故无法准确判断;
故到此为止,没有找到一个很好的能判断是否建立在堆上的对象;
3、判断是否能删除一个指针
我们可以使用list来存储new创建的对象,即重载operator new;
class HeapTracked {
public:
typedef const void* RawAddr;
class MissingAddr{};
virtual ~HeapTracked() = 0;
static void* operator new(size_t size);
static void operator delete(void *ptr);
bool isOnHeap() const;
private:
static list<RawAddr> addresses;
};
list<HeapTracked::RawAddr> HeapTracked::addresses;
void* HeapTracked::operator new(size_t size) {
void *ptr = ::operator new(size);
addresses.push_front(ptr); // 将对象添加到list
return ptr;
}
void HeapTracked::operator delete(void *ptr){
auto it = find(addresses.begin(), addresses.end(), ptr);
// 判断该对象是否在list
if(it != addresses.end()){
addresses.erase(it);
::operator delete(ptr);
}else {
throw MissingAddr();
}
}
bool HeapTracked::isOnHeap() const{
const void* ptr = dynamic_cast<const void*>(this);
auto it = find(addresses.begin(), addresses.end(), ptr);
return it != addresses.end();
}
4、禁止堆对象
若想要禁止使用new,即直接将operator new放入private中即可;
class UPNumber {
private:
static void *operator new(size_t size);
static void operator delete (void* ptr);
static void *operator new[](size_t size);
static void operator delete[](void* ptr);
};
29、灵巧指针
其外观和行为都被设计成内建指针相似的对象;
控制行为:
构造:初始化为0,避免未初始化;
析构:释放指针,防止内存泄漏;
拷贝:深拷贝;
赋值:深拷贝;
Dereferencing:取值;
template <typename T>
class SmartPtr {
public:
SmartPtr<T>(T* realPtr = 0);
SmartPtr<T>(const SmartPtr& rhs);
~SmartPtr<T>();
SmartPtr<T>& operator=(const SmartPtr<T>& rhs);
T* operator->() const;
T& operator*() const;
private:
T* ptr;
};
1、samrt指针转换为dunb指针
// 可能会导致引用计数被破坏
// 注意,一般不提供转换到dumb的隐式类型准换 *** 作符;
Tuple *ptr = Samrt<Tuple> t;
2、Smart和继承类与基类的类型转换
当添加类型转换后即可,否则错误,由于Smart指针不像dumb指针一样能无法转换
template<class T>
class SmartPtr {
public:
SmartPtr(T* realPtr = 0);
T* operator->() const;
T& operator*() const;
template<class newType>
// 类型转换
operator SmartPtr<newType>(){
return SmartPtr<newType> (realPtr);
}
private:
T* realPtr;
};
// 转换
void display(const SmartPtr<Base>& b) {
}
Smart<Sun> s(new s);
display(s);
3、Smart指针与const
dumb指针能够用non-const初始化const,也可让non-const初始化指向const而Smart却不行;
若想要让Smart有该特性,我们将添加隐式类型转换 *** 作;
- 让non-const对象包含const dumb指针,指向const Smart指针需要包含一个指向const的dumb指针;
template<class T>
class SmartPtrToConst{
protected:
union {
const T* constPtr;
T* ptr;
};
};
template<class T>
class SmartPtr :public SmartPtrToConst<T> {
};
SmartPtr<CD> ptr = new CD("test");
SmartPtrToConst<CD> pConstCD = ptr;
30、引用计数
是一个垃圾回收机制;用于跟踪堆中对象,当为0时,即释放;
允许多个相同值的对象共享该值,节省了内存,提高了运行速度由于不需要构造和析构;
1、如何共享相同值
当相同值时,读 *** 作、赋值、拷贝 *** 作都只增加引用计数,而若发生写 *** 作时,则生成独立的一份数据,防止对共享值进行修改;
class String{
public:
/** 构造函数 */
String(const char* val = "") : value(new StringVal(val)){
cout << val << endl;
}
/** 拷贝构造,只增加引用计数 */
String(const String& rhs) : value(rhs.value) {
++value->refCount;
}
/** 赋值 *** 作
* 先自我检查w
* 判断是否有共享值(引用计数是否为0)
* 则将新的值共享给该对象,且增加引用计数
* */
String& operator=(const String& rhs) {
if(value == rhs.value){
return *this;
}
if(--value->refCount == 0){
delete value;
}
value = rhs.value;
++value->refCount;
return *this;
}
// 读 *** 作
const char& operator[](int idx) const {
return value->data[idx];
}
/**
* 写 *** 作:
* 必须满足返回引用计数为1,若不为1则说明有值共享则需要
* 重新在创建一份
* */
char& operator[](int idx) {
if(value->refCount > 1) {
--value->refCount;
value = new StringVal(value->data);
}
return value->data[idx];
}
/** 当有共享时,只对引用计数-- */
~String() {
if(--value->refCount == 0) delete value;
}
private:
struct StringVal {
int refCount;
char* data;
StringVal(const char *val);
~StringVal();
};
struct StringVal* value;
};
String::StringVal::StringVal(const char *val)
:refCount(1){
data = new char[strlen(val) + 1];
strcpy(data, val);
}
String::StringVal::~StringVal() {
delete[] data;
}
2、防止通过指针接收非const []的返回值引用
通过加入shareable标志;
class String{
public:
/** 构造函数 */
String(const char* val = "") : value(new StringVal(val)){
cout << val << endl;
}
/** 拷贝构造,只增加引用计数 */
String(const String& rhs) {
if(rhs.value->shareable){
value = rhs.value;
++value->refCount;
}else{
value = new StringVal(rhs.value->data);
}
}
/** 赋值 *** 作,若相等直接将只返回,
* 判断是否有共享值(引用计数是否为0)
* 则将新的值共享给该对象,且增加引用计数
* */
String& operator=(const String& rhs) {
if(value == rhs.value){
return *this;
}
if(--value->refCount == 0){
delete value;
}
value = rhs.value;
++value->refCount;
return *this;
}
// 读 *** 作
const char& operator[](int idx) const {
return value->data[idx];
}
/**
* 写 *** 作:
* 必须满足返回引用计数为1,若不为1则说明有值共享则需要
* 重新在创建一份
* */
char& operator[](int idx) {
if(value->refCount > 1) {
--value->refCount;
value = new StringVal(value->data);
}
value->shareable = false;
return value->data[idx];
}
/** 当有共享时,只对引用计数-- */
~String() {
if(--value->refCount == 0) delete value;
}
private:
struct StringVal {
int refCount;
bool shareable;
char* data;
StringVal(const char *val);
~StringVal();
};
struct StringVal* value;
};
String::StringVal::StringVal(const char *val)
:refCount(1), shareable(true){
data = new char[strlen(val) + 1];
strcpy(data, val);
}
String::StringVal::~StringVal() {
delete[] data;
}
3、带引用计数的基类
当多个对象具有相同值的类都可以使用引用计数,故可以使用基类来完成;
// 基类
class RCObject{
public:
RCObject() : refCount(0), shareable(true){}
RCObject(const RCObject& rhs) : refCount(0), shareable(true) {}
RCObject& operator=(const RCObject& rhs){ return *this; }
virtual ~RCObject() = 0;
void addReference() { ++refCount; }
void removeReference() {
if(--refCount == 0) delete this;
}
void markUnshareable() { shareable = false; }
bool isShareable() const { return shareable; }
bool isShared() const { return refCount > 1; }
private:
int refCount;
bool shareable;
};
class String {
private:
struct StringVal : public RCObject {
char* data;
StringVal(const char* val);
~StringVal();
};
};
String::StringVal::StringVal(const char *val) {
data = new char[strlen(val) + 1];
strcpy(data, val);
}
String::StringVal::~StringVal() {
delete [] data;
}
4、自动的引用计数处理:省去addReference
和removeReference
制作一个用于引用计数的Smart指针;
String不需要拷贝构造函数和赋值 *** 作以及析构函数,由于拷贝的处理被安置在StringVal中,以及赋值、析构都交给RCPtr成员
处理;
/************************************管理引用计数************************************/
class RCObject{
public:
void addReference() { ++refCount; }
void removeReference() {
if(--refCount == 0) delete this;
}
void markUnshareable() { shareable = false; }
bool isShareable() const { return shareable; }
bool isShared() const { return refCount > 1; }
protected:
RCObject() : refCount(0), shareable(true){}
RCObject(const RCObject& rhs) : refCount(0), shareable(true) {}
RCObject& operator=(const RCObject& rhs){ return *this; }
virtual ~RCObject() = 0;
private:
int refCount;
bool shareable;
};
/************************************Smart指针************************************/
template <class T>
class RCPtr{
public:
RCPtr(T* realPtr = 0);
RCPtr(const RCPtr& rhs);
~RCPtr();
RCPtr& operator=(const RCPtr& rhs);
T* operator->() const;
T& operator*() const;
private:
T* ptr;
void init();
};
template<class T>
RCPtr<T>::RCPtr(T *realPtr) : ptr(realPtr){ init(); }
template<class T>
RCPtr<T>::RCPtr(const RCPtr<T> &rhs) : ptr(rhs.ptr){ init(); }
template<class T>
RCPtr<T>::~RCPtr<T>() {
if(ptr) ptr->removeReference();
}
template<class T>
RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs) {
if(ptr != rhs.ptr) {
if(ptr) {
ptr->removeReference();
}
ptr = rhs.ptr;
init();
}
return *this;
}
template<class T>
T* RCPtr<T>::operator->() const { return ptr; }
template<class T>
T& RCPtr<T>::operator*() const { return *ptr; }
template<class T>
void RCPtr<T>::init() {
if(ptr == 0) return;
if(ptr->isShareable() == false){
ptr = new T(*ptr);
}
ptr->addReference();
}
/************************************String************************************/
class String {
public:
String(const char *val = "") : value(new StringVal(val)) {}
const char& operator[](int idx) const { return value->data[idx]; }
char& operator[](int idx) {
if(value->isShareable()) {
value = new StringVal(value->data);
}
value->markUnshareable();
return value->data[idx];
}
private:
struct StringVal : public RCObject {
char *data;
StringVal(const char* val);
StringVal(const StringVal& rhs);
void init(const char* val);
~StringVal();
};
RCPtr<StringVal> value;
};
void String::StringVal::init(const char *val) {
data = new char[strlen(val) + 1];
strcpy(data, val);
}
String::StringVal::StringVal(const char *val) {
init(val);
}
String::StringVal::StringVal(const StringVal &rhs) {
init(rhs.data);
}
String::StringVal::~StringVal() noexcept {
delete [] data;
}
5、在现存类上增加引用计数
让库中无法修改的类中添加引用设计数的功能;
PRIptr与前面不同:
- PRIptr通过中间层的CountHolder对象指向值对象
- 且重载了operator->和operator*,当非const则,自动执行写时拷贝
将库中的类引入到自定义类中,进行管理
/************************************管理引用计数************************************/
class RCObject{
public:
void addReference() { ++refCount; }
void removeReference() {
if(--refCount == 0) delete this;
}
void markUnshareable() { shareable = false; }
bool isShareable() const { return shareable; }
bool isShared() const { return refCount > 1; }
protected:
RCObject() : refCount(0), shareable(true){}
RCObject(const RCObject& rhs) : refCount(0), shareable(true) {}
RCObject& operator=(const RCObject& rhs){ return *this; }
virtual ~RCObject() = 0;
private:
int refCount;
bool shareable;
};
/************************************Smart指针************************************/
template <class T>
class RCIPtr{
public:
RCIPtr(T* realPtr = 0);
RCIPtr(const RCIPtr& rhs);
~RCIPtr();
RCIPtr& operator=(const RCIPtr& rhs);
const T* operator->() const;
T* operator->();
const T& operator*() const;
T& operator*();
private:
struct CountHolder : public RCObject {
~CountHolder() { delete ptr; }
T* ptr;
};
CountHolder *counter;
void init();
void makeCopy();
};
template<class T>
RCIPtr<T>::RCIPtr(T *realPtr) : counter(new CountHolder){ counter->ptr = realPtr; }
template<class T>
RCIPtr<T>::RCIPtr(const RCIPtr<T> &rhs) : counter(rhs.counter){ init(); }
template<class T>
RCIPtr<T>::~RCIPtr<T>() {
counter->removeReference();
}
template<class T>
RCIPtr<T>& RCIPtr<T>::operator=(const RCIPtr& rhs) {
if(counter != rhs.counter) {
counter->removeReference();
counter = rhs.counter;
init();
}
return *this;
}
template<class T>
void RCIPtr<T>::makeCopy() {
if(counter->isShareable()) {
T* oldVal = counter->ptr;
counter->removeReference();
counter = new CountHolder;
counter->ptr = new T(*oldVal);
counter->addReference();
}
}
template<class T>
const T* RCIPtr<T>::operator->() const { return counter->ptr; }
template<class T>
T* RCIPtr<T>::operator->() { makeCopy(); return counter->ptr; }
template<class T>
const T& RCIPtr<T>::operator*() const { return *(counter->ptr); }
template<class T>
T& RCIPtr<T>::operator*() { makeCopy(); return *(counter->ptr); }
template<class T>
void RCIPtr<T>::init() {
if(counter->isShareable() == false){
T *oldVal = counter->ptr;
counter = new CountHolder;
counter->ptr = new T(*oldVal);
}
counter->addReference();
}
/************************************Widget(表示库的类不可被修改)************************************/
class Widget {
public:
Widget(int size);
Widget(const Widget& rhs);
~Widget();
Widget& operator=(const Widget& rhs);
void doThis();
int showThat() const;
};
/**
* 此处不需要声明拷贝构造函数、赋值、析构
* 将Widget引入RCWidget来管理
* */
class RCWidget{
public:
RCWidget(int size) : value(new Widget(size)) {}
void doThis() { value->doThis(); }
int showThat() { value->showThat(); }
private:
RCIPtr<Widget> value;
};
31、代理类
1、实现二维数组
二维数组的使用是[][],即我们可以通过重载[] *** 作符来实现;但C++中没有[][] *** 作符,故我们可以进行嵌套实现;
#include
#include
using namespace std;
template<class T>
class Array2D{
public:
struct Array1D{
Array1D(){}
T& operator[](int idx) {
assert(idx < m_y || idx > 0);
return dat[idx];
}
const T& operator[](int idx) const {
assert(idx < m_y || idx > 0);
return dat[idx];
}
~Array1D() {
delete [] dat;
}
T *dat;
int m_y;
};
public:
Array2D(int x, int y) ;
Array1D& operator[](int idx) {
assert(idx < m_x || idx > 0);
return data[idx];
}
const Array1D& operator[](int idx) const {
assert(idx < m_x || idx > 0);
return data[idx];
}
~Array2D() {
delete [] data;
}
private:
Array1D *data;
int m_x;
};
template<class T>
Array2D<T>::Array2D(int x, int y) : m_x(x){
data = new Array1D[x];
for(int i=0; i<x; ++i) {
data[i].dat = new T[y];
data[i].m_y = y;
}
}
int main() {
Array2D<int> a(2, 2);
/* 输入 */
for(int i=0; i<2; ++i) {
for(int j=0; j<2; ++j)
cin >> a[i][j];
}
/* 输出 */
for(int i=0; i<2; ++i) {
for(int j=0; j<2; ++j)
cout << a[i][j] << " ";
}
cout << endl;
return 0;
}
2、区分通过operator[]进行的是读 *** 作还是写 *** 作
通过proxy类,即不用通过const来区分operator[]读还是写 *** 作;
- 创建它,指定它扮演哪个字符;
- 将它作为赋值 *** 作的目标。在该情况下可以将赋值真正作用在它的扮演字符;此时,proxy类为左值;
- 用其它方式使用它,此时,代理类扮演的是右值;
我们使用[]创建返回proxy对象来代替字符,其他 *** 作推迟到知道它是读/写 *** 作;
class RCObject{
public:
void addReference() { ++refCount; }
void removeReference() {
if(--refCount == 0) delete this;
}
void markUnshareable() { shareable = false; }
bool isShareable() const { return shareable; }
bool isShared() const { return refCount > 1; }
protected:
RCObject() : refCount(0), shareable(true){}
RCObject(const RCObject& rhs) : refCount(0), shareable(true) {}
RCObject& operator=(const RCObject& rhs){ return *this; }
virtual ~RCObject() = 0;
private:
int refCount;
bool shareable;
};
template <class T>
class RCPtr{
public:
RCPtr(T* realPtr = 0);
RCPtr(const RCPtr& rhs);
~RCPtr();
RCPtr& operator=(const RCPtr& rhs);
T* operator->() const;
T& operator*() const;
private:
T* ptr;
void init();
};
class String{
public:
class CharProxy {
public:
CharProxy(String& str, int idx);
CharProxy& operator=(const CharProxy& rhs);
CharProxy& operator=(char c);
char* operator&();
const char* operator&() const;
operator char() const;
private:
String& theString;
int cIdx;
};
const CharProxy operator[](int idx) const;
CharProxy operator[](int idx);
friend class CharProxy;
private:
struct StringVal : public RCObject{
char *data;
explicit StringVal(const char* val);
StringVal(const StringVal& rhs);
void init(const char* val);
~StringVal();
};
RCPtr<StringVal> value;
};
/* 使用const_cast为了满足构造函数的需要,由于其接收一个非const的String类 */
const String::CharProxy String::operator[](int idx) const {
return CharProxy(const_cast<String&>(*this), idx);
}
String::CharProxy String::operator[](int idx) {
return CharProxy(*this, idx);
}
/** 代理类 */
String::CharProxy::CharProxy(String &str, int idx) :
theString(str), cIdx(idx){}
/** 类型转换返回右值 */
String::CharProxy::operator char() const {
return theString.value->data[cIdx];
}
/** 作为左值的拷贝 */
String::CharProxy& String::CharProxy::operator=(const CharProxy &rhs) {
if(theString.value->isShareable()) {
theString.value = new StringVal(theString.value->data);
}
theString.value->data[cIdx] = rhs.theString.value->data[rhs.cIdx];
return *this;
}
/** 写 *** 作。由于为非const成员函数,故该队向不能作为赋值的目标使用 */
String::CharProxy& String::CharProxy::operator=(char c) {
if(theString.value->isShared()) {
theString.value = new StringVal(theString.value->data);
}
theString.value->data[cIdx] = c;
return *this;
}
const char* String::CharProxy::operator&() const {
return &(theString.value->data[cIdx]);
}
char* String::CharProxy::operator&() {
if(theString.value->isShared()) {
theString.value = new StringVal(theString.value->data);
}
theString.value->markUnshareable();
return &(theString.value->data[cIdx]);
}
4、总述
不能所有的进行隐式类型转换;
proxy缺点:
- 返回值为临时对象,需要进行构造和析构,给程序增加了负担;
32、让函数根据一个以上的对象来决定怎么虚拟
当我们使用C++需要提供一种作用在多个对象上的虚函数——二重调度;
1、用虚函数加RTTI
即使用if…then…else对派生类的处理;
在实际开发过程中,若有新增派生类,则必须多该if…then…else进行维护,很麻烦!
2、只用虚函数
对虚函数进行重载多种派生子类参数,让代码难以维护;
若在大的程序内,每次修改则需要重新编译,修改代价高;
3、模拟虚函数表
使用虚函数表的特性来实现,建立映射表,获取相应的子类;
/** 基类 */
class GameObj{
public:
virtual void collide(GameObj& otherObj) = 0;
};
/** 派生类 */
class SpaceShip : public GameObj {
public:
virtual void collide(GameObj& otherObj);
private:
typedef void (SpaceShip::*HitFuncPtr)(GameObj&);
typedef map<string, HitFuncPtr> HitMap;
/** 获取子类 */
static HitFuncPtr lookup(const GameObj& whatWeHit);
};
/** 执行函数 */
void SpaceShip::collide(GameObj &otherObj) {
HitFuncPtr hfp = lookup(otherObj);
if(hfp) {
(this->*hfp)(otherObj);
}else {
throw;
}
}
/** 获取子类执行函数 */
SpaceShip::HitFuncPtr SpaceShip::lookup(const GameObj& whatWeHit) {
static HitMap collisionMap;
/* 检索子类 */
auto mapEntry = collisionMap.find(typeid(whatWeHit).name());
if(mapEntry == collisionMap.end()) return 0;
return (*mapEntry).second;
}
4、初始化模拟虚函数表
要向实现上述的map查询功能,则必须将子类都一一加入map中;
如何才能实现这些子类只添加一次;
以下代码改进:
1)使用auto_ptr进行管理,避免拷贝赋值或者释放的问题;
2)重载collide函数将其参数都修改一致;
/** 基类 */
class GameObj{
public:
virtual void collide(GameObj& otherObj) = 0;
};
class SpaceStation : public GameObj{
};
class Asteroid : public GameObj{
};
/** 派生类 */
class SpaceShip : public GameObj {
public:
virtual void collide(GameObj& otherObj);
/** 将参数修改一致以满足HitFuncPtr参数的要求 */
virtual void hitSpaceShip(GameObj& spaceShip) {
SpaceShip& otherShip = dynamic_cast<SpaceShip&>(spaceShip);
// ...
}
virtual void hitSpaceStation(GameObj& spaceStation) {
SpaceStation& otherShip = dynamic_cast<SpaceStation&>(spaceStation);
// ...
}
virtual void hitAsteroid(GameObj& asteroid) {
Asteroid& otherShip = dynamic_cast<Asteroid&>(asteroid);
// ...
}
private:
typedef void (SpaceShip::*HitFuncPtr)(GameObj&);
typedef map<string, HitFuncPtr> HitMap;
/** 初始化map */
static HitMap* initMap();
/** 获取子类 */
static HitFuncPtr lookup(const GameObj& whatWeHit);
};
/** 执行函数 */
void SpaceShip::collide(GameObj &otherObj) {
HitFuncPtr hfp = lookup(otherObj);
if(hfp) {
(this->*hfp)(otherObj);
}else {
throw;
}
}
/** 初始化map */
SpaceShip::HitMap* SpaceShip::initMap() {
HitMap *phm = new HitMap;
(*phm)["SpaceShip"] = &SpaceShip::hitSpaceShip;
(*phm)["SpaceStation"] = &SpaceShip::hitSpaceStation;
(*phm)["Asteroid"] = &SpaceShip::hitAsteroid;
return phm;
}
/** 获取子类执行函数 */
SpaceShip::HitFuncPtr SpaceShip::lookup(const GameObj& whatWeHit) {
static auto_ptr<HitMap> collisionMap(initMap());
/* 检索子类 */
auto mapEntry = collisionMap->find(typeid(whatWeHit).name());
if(mapEntry == collisionMap->end()) return 0;
return (*mapEntry).second;
}
4、使用非成员函数
当新增GameObj类时即重新编译,该如何避免呢?
只需要将映射表中包含的指针指向非成员函数即可;
当映射表移到非成员函数后,则执行函数中的参数有变,需要依次重载,切map类型即使用pair进行存储;
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)