c ++ - 抽象类和虚拟构造函数的替代

时间:2017-05-29 20:32:58

标签: c++ c++11 design-patterns abstract-class

假设我有以下代码:

class Block{
private:
  data Data;
public:
  data getData();
  Block(arg3 Arg3, arg4 Arg4);
};

实际上,有几种方法可以构建一个块,但总是使用相同的成员Data和方法getData(),唯一的区别就是如何构建块。换句话说,唯一的区别是构造函数......

不是为每个构建过程编写不同的类,我可以分解我的代码的一部分,在抽象类中定义和声明getData,如果在c ++中有一个虚拟构造函数,我可以为每个派生编写不同的东西对应于不同构建过程的类。

我对这类事情没有很多经验,所以我想知道是否有替代虚拟构造函数的东西?或者可能是一种不同的方法来进行这种分解?

PS:我知道https://isocpp.org/wiki/faq/virtual-functions#virtual-ctors但是对于我想要做的事情似乎相当复杂,这看起来很常见......我只想分解几个类之间的共享代码,这对应于除了构造函数。我想强制对应于其他构建过程的新类来实现一个新的构造函数。

有关我的具体情况的更多详情:

我有一个算法,我使用块并且它不依赖于它们的构建过程,因此我使用模板参数实现了算法,以无差别地表示其构建过程的块。但是我使用了一些方法及其构造函数,所以我需要代表块的类都有我需要的相同类型的方法和相同的构造函数来使用它们作为我的算法实现的模板参数。这就是为什么我想到了抽象类,强制一个新实现的类表示块,以便在我实现的算法中使用我需要的方法和构造函数。可能是一个糟糕的设计模式,这就是我被卡住的原因......

修改

到目前为止,感谢您的回答。我试图做一点通用但我觉得它实际上太模糊了,即使我最后给出了细节。所以这就是我的想法:我有一个Matrix类如下

// Matrix.hpp
template<typename GenericBlock> class Matrix{
  std::vector<GenericBlock> blocks;
  Matrix(arg1 Arg1, arg2 Arg2);
};

template<typename GenericBlock>
Matrix<GenericBlock>::Matrix(arg1 Arg1, arg2 Arg2){
  // Do stuff
  GenericBlock B(arg3 Arg3, arg4 Arg4);
  B.getData();
}

块实际上是压缩的,有几种方法可以压缩它们,它不会改变类Matrix中的任何内容。为了避免为每个压缩技术编写矩阵类,我使用了你看到的模板参数。所以我只需要为每个压缩技术编写一个类,但它们必须具有相同的方法和构造函数参数才能与Matrix兼容。

这就是为什么我想做一个抽象类,为每个压缩技术编写一个类。在抽象类中,我会编写Matrix中所需的所有内容,以便每个派生类都与Matrix兼容。我的示例现在的问题是:我可以在抽象类中定义getData,因为它总是相同的(例如,Data可以是行数)。派生类真正需要定义的唯一内容是构造函数。

一种解决方案是没有抽象类并使用受保护的构造函数。但它不会强制新派生的类重新实现构造函数。这就是我被困的原因。但我认为这个问题足以让其他人感兴趣。那么在这种情况下是否有替代虚拟构造函数? (可能是一个工厂模式,但对于这样一个常见问题似乎相当复杂)如果没有,是否有更好的方法来实现一个矩阵类,其块可以用不同的方式构建,即其构造函数可以彼此不同,而拥有相同的数据和一些共同的方法?

PS:我对产生低秩矩阵的压缩技术感兴趣,这就是数据总是相同的原因,而不是构建过程的原因。

4 个答案:

答案 0 :(得分:2)

到目前为止,您所分享的内容尚不清楚为什么需要抽象类或虚拟构造函数。每种构建块的方法都有一个工厂函数:

class Block {
  Data data;
public:
  Block(Data d) : data(std::move(d)) {}
  Data getData();
};

Block createABlock() { return Block{Data{1.0, 2.0, 3.0}}; }  
Block createBBlock() { return Block{Data{42.0, 3.14, 11.6}}; }

int main() {
  auto b1 = createABlock();
  auto b2 = createBBlock();
}

Live demo

也许这需要使用抽象工厂进行扩展,以便您可以传递通用块工厂:

using BlockFactory = std::function<Block()>;

int main() {   
  BlockFactory f = createABlock;
  auto b3 = f();   
}

修改: 关于您的编辑,您建议works fine。您不需要虚拟构造函数。模板类型GenericBlock只需满足模板定义的隐式接口。它不需要从特定的基类派生(尽管它可以做到)。它唯一需要的是它必须有一个构造函数,它接受一组特定的参数和一个getData方法。你所拥有的是编译时静态多态,虚函数是运行时动态多态。

继承可以正常工作,但正如我上面所说,我很想使用某种工厂。您可能不需要模拟整个Matrix类,因为只有构造函数需要工厂。如果在编译时知道工厂,则可以将其作为模板参数传递:

class Matrix {
  std::vector<Block> blocks;
 public:

  template<typename BlockFactory>
  Matrix(BlockFactory f);
};

template<typename BlockFactory>
Matrix::Matrix(BlockFactory f){

  // Do stuff...

  Block B = f();
  auto data = B.getData();
  for (auto v : data)
    std::cout << v << " ";
  std::cout << "\n";
}

int main() {
  Matrix ma(createABlock);
  Matrix mb(createBBlock);
}

Live demo

答案 1 :(得分:1)

TL:DR,但如果所有data的{​​{1}}相同,则您甚至不需要多个类,但只需要多个构造函数。

Block

答案 2 :(得分:1)

template<class T>struct tag_t{constexpr tag_t(){}; usong type=T;};
template<class T>constexpr tag_t<T> tag{};

这允许您将类型作为值传递。

struct BlockA{};
struct BlockB{};

class Block {
  enum BlockType { typeA, typeB };;
  BlockType type;
  data Data;
public:
  Block(tag_t<BlockA>, int x)
  : type(typeA), Data(x) {}
  Block(tag_t<BlockB>, int x)
  : type(typeB), Data(2*x+7) {}
/* ... */
};

块都是相同的类型。标签决定了它们的构造方式。

答案 3 :(得分:0)

虚拟构造函数没有其他选择,因为没有虚拟构造函数可供使用。我知道这很难接受,但这是事实。

无论如何,你不需要像虚拟构造函数那样存在的东西......

  

[..]唯一的区别是如何构建块。换句话说,   唯一的区别是构造函数......

如果唯一的区别在于构造函数,那么只需使构造函数获取一个参数,该参数指示需要哪种类型的块。或者,您可以使用一些以不同方式构造块的函数:

struct Block {
    private:
        Block(){}
        friend Block createWoodenBlock();
        friend Block createStoneBlock();
};

Block createWoodenBlock(){ return Block(); }
Block createStoneBlock(){ return Block(); }


int main() {
    Block woody = createWoodenBlock();
    Block stony = createStoneBlock();
}