【问题标题】:C++ map containing any type of template class object包含任何类型的模板类对象的 C++ 映射
【发布时间】:2018-03-09 10:33:41
【问题描述】:

我正在编写需要几种堆栈的代码。比如stack<int>, stack<vector<int>>, stack<map<int, string>>, stack<vector<vector<int>>等。

我有一个基类,它包含一个虚函数 foo(...参数列表...)。这个基类被用作多个派生类的父类,这些派生类的 foo() 适用于不同类型的堆栈。因此,derived1 可能正在使用 stack<int>,而 derived2 可能在各自的 foo() 中使用 stack<vector<vector<int>> 等等。

现在我将所有堆栈传递给所有 foo 函数。因此,每次要实现一个新堆栈时,我的 foo() 定义都必须在基类和所有派生类中再插入一个堆栈类型。

例如,最初我只有一个派生类,我的基类和派生类就像

class Base
{
     public:
        void foo(stack<int>) = 0;
} 

class Derived : public Base
{
     public:
        void foo(stack<int>)
        {
            ...
            ...
        }
}

现在当一个新的派生类出现时,假设使用stack&lt;vector&lt;int&gt;&gt;。我需要将我的 Base 和所有派生类 foo() 更新为

void foo(stack<int> a, stack<vector<int> b)
{
     ....
     ....
}

相反,我现在想要实现的是创建一个包含各种堆栈的StackWrapper 类,并且我可以使用不同的键来寻址不同的堆栈。

有点像

class StackWrapper
{
      stack<int> derived1;
      stack<vector<int>> derived2;
     some-return-type operator[](char ch)
     {
        switch(ch)
        {
            case 'a': return derived1;break;
            case 'b': return derived2;break;
        }
     }
}

这样,我只需要在这个 stackwrapper 类中添加一个新的堆栈类型对象,然后在不同的派生类 foo() 中使用 stackWrapperObj['a']stackWrapperObj['b'] 之类的东西。

有什么方法可以允许这样的类设计。

注意:- 我不希望在 foo() 中进行任何转换,例如返回 void* 然后再转换它,而是我想以某种方式返回一个转换对象,具体取决于我分配给不同堆栈的各种键类型。这样我就可以进行类似

的操作
stack<int> a = stackWrapperObject['a'];
stack<vector<int>> b = stackWrapperObject['b'];

【问题讨论】:

  • 是否有机会在与堆栈中包含的类型正交的各种堆栈上进行所需的操作,并使用迭代器? IE。泛泛地解决问题而不是面向对象?
  • 为什么类派生自一个通用类型?是因为你需要在运行时切换类型,还是因为你希望在编译时做不同的事情时能够重用相同的代码?
  • 使用模板类而不是继承。
  • 运行时状态不能改变变量的类型。有很多方法可以解决您的问题,但最好的方法将取决于您开始使用此系统的原因。我可以猜出你真正想要什么并解决它,但如果你解释一下你为什么要这样做,它会更容易也更有可能有用。
  • @tilak 仅描述技术细节,而不是您的问题。为什么有一个共同的基类呢?从表面上看,这似乎是个坏主意。可能您有一个理由说明这不是一个坏主意,而这个理由可以告诉我们如何解决您的问题。或者也许你认为你有这个理由,但你错了。

标签: c++ templates inheritance stl virtual-functions


【解决方案1】:

为什么还要写operator[]

struct StackWrapper
{
    stack<int> a;
    stack<vector<int>> b;
}

void Derived::foo (StackWrapper & stacks)
{
    stack<int> a = stacks.a;
    stack<vector<int>> b = stacks.b;
    // ... 
}

此时你可能不需要那些本地人,直接使用stacks.a

【讨论】:

    【解决方案2】:

    尝试使用访问者模式:使用 Caesar Shift 的基本非模板示例:


    ma​​in.cpp

    #include <iostream>
    #include <fstream>
    #include <string>
    
    #include "CaesarShift.h"
    
    int main() {
        std::string filename;
        std::cout << "Please enter the name of the input file. ";
        std::cin >> filename;
    
        std::ifstream fileIn;
        std::string   text;
    
        fileIn.open( filename );
        if ( !fileIn.is_open() ) {
            std::cout << "Failed to open file: " << filename << "." << std::endl;
        }
    
        fileIn >> text;
        fileIn.close();
    
        CaesarText caesarText;
        caesarText.addText( text );
    
    
        std::cout << "Contents of the Caesar Text before peforming a Caesar Shift:\n"
            << caesarText << std::endl;
    
        int amount = 0;
        std::cout << "Please enter the amount to shift the text ";
        std::cin >> amount;
        std::cout << "Now performing the Caesar Shift: " << std::endl;
    
        caesarShift( caesarText, amount );
    
        std::cout << "Caesar Text after performing a Caesar shift:\n"
            << caesarText << std::endl;
    
        std::ofstream fileOut;
        fileOut.open( std::string( "shifted_" + filename ) );
        if ( !fileOut.is_open() ) {
            std::cout << "Failed to open shifted_" << filename << std::endl;
        }
        fileOut << caesarText.shiftedText() << std::endl;
        fileOut.close();
    
        system( "PAUSE" );
        return 0;
    }
    

    CaesarShift.h

    #ifndef CAESAR_SHIFT_H
    #define CAESAR_SHIFT_H
    
    class CaesarText {
        std::string _originalText;
        std::string _shiftedText;
    
    public:
        CaesarText() = default;
        explicit CaesarText( const std::string& text ) :
            _originalText( text ) {}
    
        void addText( const std::string& text ) {
            _originalText = text;
        }
    
        std::string originalText() const {
            return _originalText;
        }
    
        std::string shiftedText() const {
            return _shiftedText;
        }
    
        friend void caesarShift( CaesarText& c, int amount );
    
        friend std::ostream& operator<<( std::ostream& out, const CaesarText& ceasarText );
    };
    
    #endif // !CAESAR_SHIFT_H
    

    CaesarShift.cpp

    #include "CaesarShift.h"
    #include <string>
    #include <iostream>
    #include <algorithm>
    
    std::ostream& operator<<( std::ostream& o, const CaesarText& c ) {
        o << "Original Text: " << c._originalText << "\n";
        o << "Shifted Text: " << c._shiftedText << "\n";
        return o;
    }
    
    void caesarShift( CaesarText& text, int amount ) {
        // Bound amount to the number of characters in the alphabet
        amount %= 26;
    
        // Three Different Ways To Perform The Shift //
        /*for ( std::size_t i = 0; i < text._originalText.length(); i++ ) {
            char c = text._originalText[i] + amount;
            text._shiftedText += c;
        }*/
    
        for ( auto& c : text._originalText ) {
            text._shiftedText += c + amount;
        }
    
        /*std::transform( text._originalText.begin(), text._originalText.end(),
                    std::back_inserter( text._shiftedText ),
                    [amount]( unsigned char c ) -> unsigned char { return c + amount; }
        );*/
    
    }
    

    输入:test.txt

    Hello
    

    控制台和文件输出 - 根据用户输入的偏移量:

    // Shift by 2
    Original Text: Hello
    Shifted Text: Jgnnq
    
    // Shift by 3
    Original Text: Hello
    Shifted Text: Khoor
    
    // Shift by 29 (29 % 26) = 3
    Original Text: Hello
    Shifted Text: Khoor
    

    如果您查看上面的类,它只存储两个字符串。它确实具有返回两个私有成员的构造函数和函数。将转移应用到CaesarText 的实现与类或容器本身是分开的。重载操作符也可以轻松地将这个对象发送到输出流。

    在这种特殊情况下,函数caeserShift() 引用一个CaesarText 和一个int。现在这个独立函数在class object 上执行实际的operations or calculations。这里唯一特别的是caesarShift()CaesarText 的朋友,使其能够直接访问成员,这样您就不必拥有getters 并创建不需要的额外临时文件和副本。所以最后 algorithm caesarShift() 不是 CeasarText 的成员,而是对它们进行操作。标准库中的容器和迭代器都使用了相同的模式。

    【讨论】:

      猜你喜欢
      • 2017-12-22
      • 2011-10-31
      • 2012-01-14
      • 2012-08-25
      • 2012-03-19
      • 2018-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多