C++ | 【05 技巧】More Effective C++

C++ | 【05 技巧】More Effective C++,第1张

文章目录
  • 索引
    • 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++🧡💛💚💙

26、将构造函数非成员函数虚拟化
虚拟构造函数:行为和构造相似,根据输入的数据的不同而建立不同类型的对象;
// 基类 
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、自动的引用计数处理:省去addReferenceremoveReference
制作一个用于引用计数的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进行存储;

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/675980.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-19
下一篇 2022-04-19

发表评论

登录后才能评论

评论列表(0条)

保存