2017-05-29 69 views
1

让说我有下面的代码:C++ - 抽象类和替代虚拟构造

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

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

而不是为每个构建过程编写一个不同的类,我可以分解我的代码的一部分,定义和声明抽象类中的getData,如果有这样的事情作为C++中的一个虚拟构造函数,我可以针对与不同构建过程相对应的每个派生类编写不同的代码。

我对这种事情没有太多经验,所以我想知道是否有虚拟构造函数的替代方案?或者可能是一种不同的方式来做这种分解?

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

更多关于我的特殊情况细节:

我有一个算法,其中我用块,它不依赖于他们的建设过程中,所以我一直在使用一个模板参数漠然表示块实现的算法其建设过程。但是我使用了一些方法及其构造函数,所以我需要表示块的类都具有我需要的相同类型的方法,以及使用它们作为我的算法实现的模板参数的相同构造函数。这就是为什么我想到抽象类,强制一个新实现的代表块的类在我实现的算法中拥有我需要的方法和构造方法。可能这是一个糟糕的设计模式,这就是为什么我坚持......

编辑

谢谢你的答案为止。我试图成为一个通用的,但我觉得它实际上太模糊了,即使我最后提供的细节。所以这是我想的事:我有一个矩阵类如下

// 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:我对产生低秩矩阵的压缩技术感兴趣,这就是为什么数据总是相同的原因,而不是构建过程。

+3

为什么不只是有多个构造函数?在我看来,你是过于复杂的事情。 –

+1

如果您想在运行时配置模块构建策略 - 为什么不使用抽象工厂? –

+0

@JesperJuhl你是什么意思? –

回答

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只需满足由模板定义的隐式接口。它不需要从特定的基类派生(尽管它可以)。它唯一需要的是它必须有一个构造函数,它需要一组特定的参数和一个方法。你所拥有的是编译时静态多态,虚函数是针对运行时动态多态的。

继承将正常工作,但正如我上面所说,我会试图使用某种工厂。您可能不需要对整个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

谢谢你的回答。我想迫使新派生的类以“矩阵”类中需要的方式定义构造函数,这就是为什么我想到“虚构构造函数”(不存在)的原因。但正如你所说,无论如何,GenericBlock必须满足隐式接口,我想我是过于复杂的东西。我最终做了你在编辑中建议的内容。 –

1

TL:DR,但是如果data对于所有Block都是相同的,那么您甚至不需要多个类,但只需要多个构造函数。

class Block 
{ 
    enum { type1, type2, type3 }; 
    int type; 
    data Data; 
public: 
    Block(int x) 
    : type(type1), Data(x) {} 
    Block(std::string const& str) 
    : type(type2), Data(str) {} 
    Block(data const*x) 
    : type(type3), Data(data) {} 
    /* ... */ 
}; 
+0

我需要构造函数具有相同的参数,实际上,所有构建过程都采用相同的参数。 –

+0

正如我在我的问题结束时所说的,我想将这些块用作我实现的算法的模板参数,以便它们需要具有构造函数的相同参数列表。 –

+3

@PierreMarchand只有现在我阅读你的编辑和最新评论。 tbh我根本听不懂。在调用构造函数时,你的算法如何不依赖于块的构建方式,并且这个调用对于所有类型的块应该是相同的......在什么时候决定应该创建哪种类型的块? – user463035818

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(); 
} 
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) {} 
/* ... */ 
}; 

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