使用C ++模板创建具有自定义组件的类

时间:2010-11-14 17:07:48

标签: c++ templates

让我通过一个例子来解释我所要求的。想象一下,我有一个汽车课。 现在,这辆车可能还有很多额外功能:

  • 4门而不是2门
  • 自动门锁
  • 4轮驱动

我想用这些选项的任意组合创建类。任何这些选项都需要一些数据成员。想象一下,这个班级现在看起来像这样:

class Car {
public:
  bool FourDoors;
  bool AutomaticDoorLocking;
  bool FourWheelDrive;

  Door doors[4];  //4 only needed if FourDoors=true
  DoorLockingElectronic doorElectronic; //Only needed if AutomaticDoorLocking=true
  TransmissionsShafts[4]; //4 only needed for FourWheelDrive=true

  void lockDoors() {
    if (AutomaticDoorLocking) {
      doorElectronic.lockDoors();
    } else {
      // Do manual door locking
    }
  }
};

到目前为止一切都那么好,但现在我想制造很多车,这么多内存变得至关重要。而且我不需要大多数汽车的大部分额外功能。 我可以创建一个基类,并派生启用或禁用这些选项的类。 但是我必须创建2 ^ {#extras}类来创建所有可能的组合,并且有很多双重代码。

所以我想也许可以使用模板? (这是问题)。

我可以想象有一个标志模板,并像这样重写lockDoors:

template<int flags>
void Car<flags>::lockDoors() {
  if (flags | AutomicDoorLockingFlag) {
    doorElectronic.lockDoors();
  } else {
    // Do manual door locking
  }
}

奇妙!但是Car&lt; 0&gt;类Car仍然需要很多不必要的空间。所以:

我可以根据模板参数以某种方式包含或排除类成员吗?

其他想法也欢迎如何应对这种情况!

5 个答案:

答案 0 :(得分:6)

您想要使用策略类:

class FourDoorPolicy { Door m_doors[4]; ... };
class TwoDoorPolicy { Door m_doors[2]; ... };

class AutoDoorLockingPolicy { ... };
class ManualDoorLockingPolicy { void lockDoors(); ... };

class FourWheelDrivePolicy { TransmissionShafts m_shafts[4]; ... };
class TwoWheelDrivePolicy { TransmissionShafts m_shafts[2]; ... };

template <class DoorPolicy = TwoDoorPolicy,
          class LockingPolicy = ManualDoorLockingPolicy,
          class DrivePolicy = TwoWheelDrivePolicy>
class Car : public DoorPolicy, public LockingPolicy, public DrivePolicy
{
  ...
};

将所有特定于策略的内容(例如lockDoors()函数)放在策略类中而不是Car类中。 Car类继承了这些,这是一种组合形式(即您将所有功能构建到Car类中)。

请注意,您应该为所有策略类提供protected,非virtual析构函数,以便它们只能作为派生类的一部分进行实例化。

然后以正常的模板方式实例化自定义汽车:

Car<FourDoorPolicy, AutoDoorLockingPolicy, TwoWheelDrivePolicy> myCar;

当然,您可以使用typedef来帮助解决这个问题(C ++ 0x中的模板别名也会有很大帮助)。

请参阅:Policy-based Design

答案 1 :(得分:1)

你可能应该研究Policy-based design。基本上,它包括作为策略类中的外部化行为,并使用适当的策略实例化模板汽车对象。策略类负责封装给定的行为。

从实现的角度来看:Car成为一个模板,其中每个类型参数对应于给定的策略(例如:DoorLockingPolicy)。然后,您可以根据您选择的类型“配置”您的汽车模板:ManualDoorLockingPolicyAutomaticDoorLockingPolicy

template<class DoorLockingPolicy /*, class DoorsPolicy, ... */>
class Car : DoorLockingPolicy
{
public:
 void lockDoors()
 {
  /* ... */
  DoorLockingPolicy::lockDoors();
 }
};

struct ManualDoorLockingPolicy
{
 void lockDoors() { /* ... */ }
};

struct AutomaticDoorLockingPolicy
{
 void lockDoors() { /* ... */ }
};

int main()
{
 Car<ManualDoorLockingPolicy> car1;
 Car<AutomaticDoorLockingPolicy> car2;
}

从性能的角度来看,基于策略的设计是实现“不为不使用的东西付费”的好方法:

  • 可以内联对策略类的调用,不会产生任何额外费用
  • Car模板可以从其策略中私下继承,并从空基优化中受益。

现代C ++设计(Andrei Alexandrescu)再一次对这个主题进行了很好的阅读。

答案 2 :(得分:0)

一种可能性是引入要素类。要素类将具有某种唯一标识符(我已经使用int作为它的地狱,但boost::uuids::uuid将更为可取。除了定义某种功能外,它什么也没做:

class Feature
{
private:
    int m_nUniqueID;

protected:
    Feature(int _uniqueID) : m_nUniqueID(_uniqueID) {};
    virtual ~Feature(){};

public:
    const int& getUniqueID const {return(m_nUniqueID);};

}; // eo class Feature

由此,我们可以得出更具体的特征:

class DoorsFeature : public Feature
{
private:
    int m_nDoors;

public:
    static const int UniqueId;
    DoorsFeature(int numDoors) : Feature(UniqueId), m_nDoors(numDoors){};
    virtual ~DoorsFeature(){};

    void lockDoors() { /* lock the doors */ };
}; // eo class DoorsFeature

class ABSFeature : public Feature
{
public:
    static const int UniqueId;
    ABSFeature() : Feature(UniqueId){};
    virtual ~ABSFeature(){};
}; // eo class ABSFeature

以及汽车可以拥有的任何功能。注意我不会将 wheel 作为一个特征,因为,尽管数量可能不同,但所有汽车都有轮子。我指的是各种各样的特性,例如电子门,ABS,等等。突然间,你的车成了一个更简单的容器:

class Car
{
private:
    int m_nWheels;
    std::string m_Colour;
    std::vector<Feature> m_Features;

protected:

public:
    Car();
    ~Car();

    void addFeature(Feature& _feature) {m_Features.push_back(_feature);};
    Feature getFeature(int _featureId) const;

    void lockDoors()
    {
        DoorsFeature& doorsFeature(static_cast<DoorsFeature&>(getFeature(DoorsFeature::UniqueId)));
        doorsFeature.lockDoors();
    } // eo lockDoors
}; // eo class Car

鉴于此,您还可以更进一步,引入命名功能集(非常类似于您从经销商/制造商处获得的选项包),可以自动应用于汽车或各种品牌,型号和系列

显然,我已经离开了很多。您可能希望将汽车的引用传递给每个功能,或者不这样做。

答案 3 :(得分:0)

我认为问题在于你正在尝试定义一个能够代表所有可能版本的“Car”的单个类,这意味着每个实例都包含能够代表所有可能的汽车的成员数据。这个问题在很久以前就被传统的继承所解决了。

定义基类中所有汽车共有的功能。然后派生添加功能的特定类(以及增加内存占用的成员变量)。只需通过实例化正确的子类即可最小化内存。每个实例仅包含对该特定类型的Car重要的成员。

答案 4 :(得分:-1)

尝试重写代码以使用vector而不是数组。你可以只使用你需要的空间,而且也更容易。

#include <vector>
#include <memory>
class Car
{
public:
  int getDoorCount() { return doors.size(); }
  bool isFourWheelDrive() { return transmissionShafts.size() == 4; }
  bool areDoorsAutoLocking() { return automaticDoorLocking.get() != NULL; }
  void lockDoors() {
    if (automaticDoorLocking.get() != NULL) {
      automaticDoorLocking->lockDoors();
    } else {
      // Do manual door locking
    }
  }

private:
  std::vector<Door> doors;
  std::vector<TransmissionsShafts> transmissionShafts;
  std::auto_ptr<DoorLockingElectronic> automaticDoorLocking;

};

注意Car现在如何支持掀背车(5门)。