C++的35个技巧阅读笔记(四)

文章目录

  • 28.Smart Pointers(智能指针)
  • 29.Reference counting(引用计数)
  • 30.Proxy classes(替身类、代理类)
  • 31.让函数根据一个以上的对象类型来决定如何虚化

系列文章:
C++的35个技巧阅读笔记(一)
C++的35个技巧阅读笔记(二)
C++的35个技巧阅读笔记(三)

28.Smart Pointers(智能指针)

所谓smart pointers,是“看起来,用起来、感觉起来都像内建指针,但提供更多机能”的一种对象。当你以smart Pointer取代C++的内建指针,你将获得以下各种指针的控制权:

  • 1、构造和析构。通常给smart pointer一个默认值0,以避免“指针未获初始化”的问题。
  • 2、复制和赋值。你可以控制指针进行深复制或者浅赋值。
  • 3、解引dereferencing(取出指针所指向的内容)。当取用smart Pointer所指之物时,你有权决定发生什么事情。
    smart Pointer由template产生,由于它就像内建指针一样,所以它必须有“强烈的类型性”。

29.Reference counting(引用计数)

  • 1、使用引用计数后,对象便拥有了它自己,一旦不再有任何人使用它,它便自动销毁自己。也因此,reference counting构建出垃圾回收机制的一个简单形式。
  • 2、此计数有两个动机:第一为了简化堆对象周边的簿记工作; 第二是为了实现一种常识,所有等值对象共享同一实值,不仅节省内存,也使程序速度加快。

将一个struct嵌套放进一个class的private段落内,可以很方便地让该class的所有members有权处理这个struct,而又能够禁止任何其他人访问这个struct(当然,class的friend不在此限)。

class String
{
public:
    String(const char *initValue = " ");
    String(const String& rhs);
    ~String();
private:
	StringValue* value;//类中成员变量
	
    struct StringValue {
    int refCount;
    char *data;
    StringValue(const char *initValue);//struct中的构造函数
    ~StringValue();
    };
...
};
String::~String()
{
    if(--value->refCount == 0) delete value;//引用计数为0进行delete
}
String::String(const char *initValue) : value(new StringValue(initValue)) //有参数构造函数1
{}
String::String(const String& rhs) : value(rhs.value) //带计数的有参数构造函数2
{
    ++value->refCount;//计数
}
String::StringValue::StringValue(const char* initValue) : refCount(1)//共享标志的构造函数3
{
    data = new char[strlen(initValue) + 1);
    strcpy(data, initValue);
}
String::StringValue::~StringValue()
{
    delete [] data;
}

30.Proxy classes(替身类、代理类)

  • 1.多维数组。凡用来代表(象征)其他对象的对象,常被称为proxy object(替身对象),而用以表现proxy objects者,我们称为proxy classes。
  • 2.代理类最神奇的功能是区分通过operator[]进行的是读操作还是写操作,它的思想是对于operator[]操作,返回的不是真正的对象,而是一个 proxy类,这个代理类记录了对象的信息。
    将它作为赋值操作的目标时,proxy类扮演的是左值;用其它方式使用它时,proxy类扮演的是右值。用赋值操作符来实现左值操作,用隐式类型转换来实现右值操作。
  • 3.局限:用proxy类区分operator[]作左值还是右值的局限性:要实现proxy类和原类型的无缝替代,必须声明原类型的一整套操作符;另外,使用proxy类还有隐式类型转换的所有缺点。
class Animal
{
public:
    virtual void Eat() = 0;

    //copy函数,构造一个基于自身对象类型的对象
    virtual Animal* copy() const = 0;

    virtual ~Animal() {}
};

class Cat : public Animal
{
public:
    virtual void Eat()
    {
        std::cout << "cat eat." << std::endl;
    }

    virtual Animal* copy() const
    {
        // 返回一个以自身作为参数构造的Cat类型的对象
        return new Cat(*this);
    }
};

class Dog : public Animal
{
public:

    virtual void Eat()
    {
        std::cout << "dog eat." << std::endl;
    }

    virtual Animal* copy() const
    {
        // 返回一个以自身作为参数构造的Dog类型的对象
        return new Dog(*this);
    }
};

class Bird : public Animal
{
public:

    virtual void Eat()
    {
        std::cout << "bird eat." << std::endl;
    }

    virtual Animal* copy() const
    {
        // 返回一个以自身作为参数构造的Bird类型的对象
        return new Bird(*this);
    }
};

//代理类
class AnimalSurrogate
{
public:
    AnimalSurrogate() :pa(NULL) {}

    AnimalSurrogate(const Animal& ani)
    {
        pa = ani.copy();
    }

    //拷贝构造
    AnimalSurrogate(const AnimalSurrogate& ani_srg)
    {
        pa = ani_srg.pa != nullptr ? ani_srg.pa->copy() : nullptr;
    }

    ~AnimalSurrogate()
    {
        if (pa != nullptr)
        {
            delete pa;
            pa = nullptr;
        }
    }

    //重载 = 操作符
    AnimalSurrogate& operator=(const AnimalSurrogate& ani_srg)
    {
        if (this != &ani_srg)
        {
            delete pa;
            pa = ani_srg.pa != nullptr ? ani_srg.pa->copy() : nullptr;
        }
        return *this;
    }

    //将基类中的公共函数搬过来,这样就可以通过代理类直接访问这些方法
    void Eat()
    {
        if (pa == nullptr)
        {
            throw "empty AnimalSurrogate.Eat()";
        }
        return pa->Eat();
    }

private:
    Animal* pa;//存储基类的指针
};

通过代码可以看出来,所谓的代理类,就是构造一个新的类,这个类中包含关联的基类类型的指针,该指针可以指向不同类型但又相互关联的子类对象,通过指针可以转调对象的方法,同时实现内存的管理 . 代理类的实用方法如下:

Cat cat;
Dog dog;
Bird bird;

arr[0] = AnimalSurrogate(cat);
arr[1] = AnimalSurrogate(dog);
arr[2] = AnimalSurrogate(bird);

arr[0].Eat();//输出 cat eat.
arr[1].Eat();//输出 dog eat.
arr[2].Eat();//输出 bird eat.

总结:代理类的的每个对象都代表另一个对象,该对象可以使位于一个完成继承层次中的任何类的对象。通过在容器中用代理对象而不是对象本身的方式,实现容器中存放不同类型的对象。

使用代理类的优缺点如下:

  • 优点:使用代理类比直接在容器中存放不同对象的指针更安全,便于实现内存管理。
  • 缺点:内存开销太大,放入容器的每个对象都需要拷贝一次,不够灵活。

延伸:
为了避免对象的拷贝,可以通过句柄类来实现,C++句柄类: 主要通过引用计数和Copy On Write实现,这两种思想还是很经典的,垃圾回收、智能指针的实现都有借鉴这两种思想。句柄类参考

31.让函数根据一个以上的对象类型来决定如何虚化

我们需要一种作用在多个对象上的虚函数。这类型问题,在C++中被称为二重调度问题,下面介绍几种方法解决二重调度问题。见书:<35 Eiffiective C++>

你可能感兴趣的:(C++技巧)