包含任何类型模板 class 对象的 C++ 映射
C++ map containing any type of template class object
我正在编写代码,我们需要几种堆栈。喜欢stack<int>, stack<vector<int>>, stack<map<int, string>>, stack<vector<vector<int>>
等
我有一个基 class,它包含一个虚函数 foo(...参数列表...)。此基础 class 被用作多个派生 class 的父级,其 foo() 适用于不同类型的堆栈。所以 derived1 可能正在使用 stack<int>
而 derived2 可能在它们各自的 foo().
中使用 stack<vector<vector<int>>
等等
现在我正在将所有堆栈传递给所有 foo 函数。因此,每次要实现一个新堆栈时,我的 foo() 定义都必须在 base 以及所有派生的 classes.
中插入一个堆栈类型
例如,最初我只有一个派生 class 我的基础和派生 classes 就像
class Base
{
public:
void foo(stack<int>) = 0;
}
class Derived : public Base
{
public:
void foo(stack<int>)
{
...
...
}
}
现在,当一个新的派生 class 出现时,假设使用 stack<vector<int>>
。我需要将我的 Base 和所有派生的 classes foo() 更新为
void foo(stack<int> a, stack<vector<int> b)
{
....
....
}
相反,我现在想要实现的是创建一个 StackWrapper
class,其中包含各种堆栈,我可以使用不同的键来处理不同的堆栈。
类似于
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 class 中添加一个新的堆栈类型对象,然后在不同的派生 class 中使用 stackWrapperObj['a']
或 stackWrapperObj['b']
之类的东西]' foo().
有什么方法可以实现这样的class设计。
注意 :- 我不想像 returning void*
那样在 foo() 中做任何转换,然后稍后再转换它,我想以某种方式 return a根据我分配给不同堆栈类型的各种键以某种方式铸造对象。这样我就可以进行
之类的操作
stack<int> a = stackWrapperObject['a'];
stack<vector<int>> b = stackWrapperObject['b'];
为什么要费心去写 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;
// ...
}
到时候你可能不需要那些locals,直接用stacks.a
就可以了
尝试使用访问者模式:使用 Caesar Shift 的基本非模板示例:
main.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
如果你看一下上面的 class 它只存储了两个字符串。它确实有 return 这两个私有成员的构造函数和函数。将移位应用到 CaesarText
的实现与 class 或容器本身是分开的。重载运算符也可以轻松地将此对象发送到输出流。
在这种特殊情况下,函数 caeserShift()
引用了 CaesarText 和 int。现在这个独立函数在 class object
上执行实际的 operations or calculations
。这里唯一特别的是 caesarShift()
是 CaesarText
的朋友,使它能够直接访问成员,这样您就不必拥有 getters
并创建不需要的额外临时文件和副本。所以最后 algorithm
caesarShift()
不是 CeasarText
的成员,但对它们进行操作。整个标准库都使用了相同的模式,包括容器和迭代器。
我正在编写代码,我们需要几种堆栈。喜欢stack<int>, stack<vector<int>>, stack<map<int, string>>, stack<vector<vector<int>>
等
我有一个基 class,它包含一个虚函数 foo(...参数列表...)。此基础 class 被用作多个派生 class 的父级,其 foo() 适用于不同类型的堆栈。所以 derived1 可能正在使用 stack<int>
而 derived2 可能在它们各自的 foo().
stack<vector<vector<int>>
等等
现在我正在将所有堆栈传递给所有 foo 函数。因此,每次要实现一个新堆栈时,我的 foo() 定义都必须在 base 以及所有派生的 classes.
中插入一个堆栈类型例如,最初我只有一个派生 class 我的基础和派生 classes 就像
class Base
{
public:
void foo(stack<int>) = 0;
}
class Derived : public Base
{
public:
void foo(stack<int>)
{
...
...
}
}
现在,当一个新的派生 class 出现时,假设使用 stack<vector<int>>
。我需要将我的 Base 和所有派生的 classes foo() 更新为
void foo(stack<int> a, stack<vector<int> b)
{
....
....
}
相反,我现在想要实现的是创建一个 StackWrapper
class,其中包含各种堆栈,我可以使用不同的键来处理不同的堆栈。
类似于
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 class 中添加一个新的堆栈类型对象,然后在不同的派生 class 中使用 stackWrapperObj['a']
或 stackWrapperObj['b']
之类的东西]' foo().
有什么方法可以实现这样的class设计。
注意 :- 我不想像 returning void*
那样在 foo() 中做任何转换,然后稍后再转换它,我想以某种方式 return a根据我分配给不同堆栈类型的各种键以某种方式铸造对象。这样我就可以进行
stack<int> a = stackWrapperObject['a'];
stack<vector<int>> b = stackWrapperObject['b'];
为什么要费心去写 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;
// ...
}
到时候你可能不需要那些locals,直接用stacks.a
就可以了
尝试使用访问者模式:使用 Caesar Shift 的基本非模板示例:
main.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
如果你看一下上面的 class 它只存储了两个字符串。它确实有 return 这两个私有成员的构造函数和函数。将移位应用到 CaesarText
的实现与 class 或容器本身是分开的。重载运算符也可以轻松地将此对象发送到输出流。
在这种特殊情况下,函数 caeserShift()
引用了 CaesarText 和 int。现在这个独立函数在 class object
上执行实际的 operations or calculations
。这里唯一特别的是 caesarShift()
是 CaesarText
的朋友,使它能够直接访问成员,这样您就不必拥有 getters
并创建不需要的额外临时文件和副本。所以最后 algorithm
caesarShift()
不是 CeasarText
的成员,但对它们进行操作。整个标准库都使用了相同的模式,包括容器和迭代器。