C++

class类型作为另一个类的成员

"更优雅的多态"

Posted by Simon on September 14, 2020

“Better code, better life. ”

class类型作为另一个类的成员

最近在写的一个需求,在网络通信时,有多种消息类型需要被创建,于是想着用继承的方式把代码写的优雅一些,这让我想到了之前写python的时候的一种语法,现在尝试用C++来实现一下。

python里,一个类(class)可以作为另一个类的成员,从而实现运行时多态,来看一段代码:

class Dog():
    def show(self):
        print("dog")
        
class Cat():
    def show(self):
        print("cat")
        
class Pet():
    def __init__(self, pet_type):
        self.type = pet_type
        
    def get_one(self):
        return self.pet_type()
    
pet = Pet(Dog)
pet.show()

Out:

dog

看完这段代码你第一时间会想到什么?工厂模式!没错,如果把Pet类改名为PetFactory是不是就有点类似工厂模式了?

但是从语法角度上讲,C++无法将一个类作为参数传递,但是我们有模板呀~,所以上面的python代码很容易用模板来实现:

class Pet{
public:
	virtual void show() = 0;    
}

class Dog : public Pet{
public:
    void show() override {
        printf("dog");
    }
}

class Cat : public Pet{
public:
    void show() override {
        printf("cat");
    }
}

template<class T>
class PetFactory{
    static_assert(std::is_base_of<Pet, T>::value, "T must derived from Pet!");
public:
    static T *getOne(){
     	return new T();   
    }
}

int main(){
	auto pet = PetFactory<Dog>::getOne();
    pet->show()
}

Out:

dog

从功能上讲,以上代码几乎完美的实现了开篇的那段python代码,但是!我们依然没有实现把class作为参数传递!

把类型当作成员

把类型当作成员乍一听起来好像无法实现,但是万能的C++有自己的一套迂回的做法。开头的python代码里为什么需要把一个类当作函数的参数传递呢?无非是想要实现工厂类的注册功能,然后来实例化一个个具体的类型。

单就这一个函数:

def get_one(self):
    return self.pet_type()

我们完全可以将pet_type当作一个函数,这个函数会返回一个注册过的示例,具体如何实现呢?重载operator ()就行了!

看代码:

class Pet;
using PetPtr = std::unique_ptr<Pet>;
using PetCreator = std::function<PetPtr()>;

template<class T>
struct ConstructHelper{
    static_assert(std::is_base_of<Pet, T>::value, "T must derived from Pet!");
    
    PetPtr operator()(){
        return PetPtr(new T);
    }
}

class Pet{    
public:
	virtual void show() = 0;    
}

class Dog : public Pet{
public:
    void show() override {
        printf("dog");
    }
}

class Cat : public Pet{
public:
    void show() override {
        printf("cat");
    }
}

template<class T>
class PetFactory{
private:
	auto petType = ConstructorHelper<T>();
    
public:
    static PetPtr getOne(){
     	return petType();   
    }
}

利用C++可以重载operator ()的特性实现了前述功能!