Closed. This question needs to be more
focused。它当前不接受答案。
想改善这个问题吗?更新问题,使其仅关注
editing this post的一个问题。
2年前关闭。
Improve this question
好的,所以我尝试了在c++中实现简单的单字母替换密码(如Caesars),有向图(如playfair),多字母字体(如autokey,vigenre)和其他一些{不使用类}。现在我想将所有这些密码和其他一些密码放在一起,并将其打包到一个项目中。我已经开始编写几行代码,但是我不确定如何设计它。这是我的类(class)样子。
我的前端
//main.cpp包含一些交换条件,可以选择正确的密码进行加密。
//cipher.cpp implements class cipher.In a crude format the class looks like
class cipher
{
protected:
string plaintxt,ciphertxt;
public:
virtual bool encrypt()=0;
virtual bool decrypt()=0;
virtual bool tabulate()=0;
}
此类由cipher.h接口(interface)
//mono_alphabetic.cpp implemants the class mono_alpha
class mono_alpha : public cipher
{
protected:
map<string,string> Etable,Dtable;
public:
bool Encrypt();
bool Decrypt();
}
现在我在这里使用一个atbash密码的简单示例,对于那些不知道atbash密码是什么的人来说,这是一种加密模式,其中给定字符串中的每个字符都使用与逆字母顺序排列。例如。 A-> Z Z-> A B-> Y M-> N,依此类推。
class atbash : public mono_alpha
{
public:
bool tabulate(); // makes a hash map were A is mapped to Z M to N e.t.c
atbash(string&); // accepts a string and copies it to string plaintxt.
}
这是一个非常粗糙的例子。这里只提供类(class)设计,以下是我的一些疑问。
实现:我将从用户那里接收一个字符串,然后将其传递给atbash类的构造函数,然后将其复制到从基类密码继承的字符串数据成员plaintxt中。然后我会从构造函数中调用函数tabulate,现在我有两个选择,要么tabulate()生成加密表的哈希表并将其存储在Etable中,要么它也可以生成解密表。在使用atbash密码的情况下,这些是一个,但相同。但是,一般的单字母替换密码的情况又如何呢?我将如何强制制表函数创建其中一个。
我的想法是将构造函数的字符参数传递给构造函数,该函数描述给定的字符串是要加密还是解密,并相应地将其保存在plaintxt或ciphertxt中。构造函数还将这个字符参数传递给制表函数相应地加密或解密表。这样好吗?
关于如何改善这一点的任何建议?
接口(interface):我实现main.cpp中所有这些密码接口(interface)的方法是使用swith case。
switch(chosen_value)
{
case 1: cout<<"atbash encryption";
cipher*ptr = new atbash ("a string");
// ptr->tabulate(); if it isn't being called directly from the constructor.(here it is)
case 2:
cout<< "caeser's cipher";
.....................
.
.....
}
有没有更好的方法可以在不使用开关盒的情况下实现这一点。
还如您所见,我已经使用指向派生类的对象的基类指针来执行此操作。我知道这里没有必要,我可以简单地通过声明一个对象来进行操作。通过基类指针引用对象真的有真正的重要性吗?
我听说这些基类指针有时可以成为现实中的救星!如果是这样,请直接指导我简化编码的场景。在这种特殊情况下,在基类中声明纯虚函数没有任何用处吗?
我应该像在这里一样继续将类实现分成单独的文件,还是应该将所有这些代码压缩在一个main.cpp中,这将使继承变得容易得多,因为您不必使用头文件。
请指导我。我在编码方面的专业经验为零,很乐意在这里提出您的意见。
一些想法,没有特别的顺序
具有不同的加密和解密类。那将解决您对使用什么的疑问。因此,密码基类成为将字符串转换为其他字符串的基类。 (不是模式专家,但我相信这是Command Pattern)
让对象代表算法的好处是它可以具有状态。如果对象的创建成本很高,则可能需要添加reset()方法,以便能够在新的执行中重用该对象。
您可以使用抽象operator()使基类成为函数对象。该operator()在每个特定的加密和解密类中实现。使用它可以让您将此类作为函数进行处理(不利之处是您可能不清楚自己在做什么)。
通过指向基类的指针(或引用或智能指针)处理所有内容都是正确的
为了创建正确的操作类型,请创建一个Factory类(这也是一种模式)。这可以是带有创建者方法的类,您可以在其中指定算法和加密/解密方向。 Factory返回指向基类的指针,指向适当的实现。
实现可以是 vector 或 map 或某些特定工厂对象的数组(其工作是实例化不同类型的算法对象)...或者,您可以在每个派生类上使用静态方法并存储一个指针结构中的方法。
结构( vector / map /数组/其他)用于快速选择正确的算法。如果算法数量很少,则使用switch语句可能很好。该结构包含在Factory类中,并在其构造函数上初始化。
您必须注意所创建对象的生命周期。对象是由工厂创建的,但是谁应该销毁它们呢?
考虑您将用来代表加密/解密消息的内容,以及它们变得不可打印或变得太大。
这里有很多设计决策,很多折衷取决于不同的事物。
希望以上几行给您一些思路。
编辑:添加一个更具体的示例
我们将从
Operation
类开始。假设我们可以同时使用相同的API调用加密器和解密器
class Operation {
public:
virtual ~Operation() { }
virtual std::string operator()(const std::string &input)=0;
virtual void reset() { }
};
注意事项:
假设API为字符串输入,则给出字符串输出。这是operator()
纯虚拟方法。
添加了虚拟析构函数。我们将主要处理对Operation的引用。但是,算法的实现需要销毁它们自己的东西,因此析构函数必须是虚拟的,以便在删除Operation指针时,它也将调用派生类的析构函数。
添加了reset()
方法。这具有不执行任何操作的默认实现。潜在派生的类可能存储状态,此方法旨在将操作返回到其初始步骤,这样您就不必废弃它并创建另一个操作。
现在,一些派生类:
class MyEncoder: public Operation {
public:
static Operation *create() {
return new MyEncoder();
}
std::string operator()(const std::string &input) {
// Do things.
return std::string();
}
};
class MyDecoder: public Operation { ... };
class OtherEncoder: public Operation { ... };
class OtherDecoder: public Operation { ... };
我只显示完整的
MyEncoder
。我们看到一个静态方法
create
,我们将在后面讨论。
算法的实现发生在
operator()
的实现上
你可以:
将状态保留在MyEncoder的属性中
初始化构造函数上的内容
...甚至可能破坏析构函数中的东西。
可能包括reset()方法的实现,以在另一个调用中重用该对象。
现在为工厂:
class OperationFactory {
public:
enum OperationDirection {
OD_DECODER=0,
OD_ENCODER
};
enum OperationType {
OT_MY=0,
OT_OTHER
};
....
};
刚刚声明了该类和几个枚举,以帮助我们区分编码器和解码器以及我将要使用的两种算法。
我们需要一些地方来存储东西,因此Factory类以以下结尾:
class OperationFactory {
public:
...
private:
typedef Operation *(*Creator)();
typedef std::map<OperationType,Creator> OperationMap;
OperationMap mEncoders;
OperationMap mDecoders;
};
这里:
第一个typedef为函数指针命名。此函数不带任何参数,并且返回指向Operation
的指针。静态方法与函数相同(至少在函数指针方面)...因此,该typedef允许我们为上面使用的神秘create()
静态方法命名。
第二个typedef只是冗长的std::map定义的快捷方式。这是从OperatonType
到Creator
函数的映射。
我们定义了两个映射,一个用于Encoder,一个用于Decoder。您可以设计一种不同的方式。
这样,我们可以为用户提供一些获取所需内容的方法:
class OperationFactory {
public:
...
Operation *getOperation(OperationDirection _direction,OperationType _type) const {
switch(_direction) {
case OD_DECODER:
return getDecoder(_type);
case OD_ENCODER:
return getEncoder(_type);
default:
// Or perhaps throw an exception
return 0;
}
}
Operation *getEncoder(OperationType _type) const {
OperationMap::const_iterator it=mEncoders.find(_type);
if(it!=mEncoders.end()) {
Creator creator=it->second;
return (*creator)();
} else {
// Or perhaps throw an exception
return 0;
}
}
Operation *getDecoder(OperationType _type) const {
.... // similar but over the mDecoders
}
....
};
因此,我们在 map 中查找OperationType并获得了指向函数(
Creator
)类型的指针,我们可以调用此函数
(*creator)()
以获得所需的
Operation
的实例。
(*creator)()
上的一些单词:
creator
的类型为Creator
...,因此它是函数的指针。
(*creator)
是函数(就像p
是int *
,*p
是int类型一样)...
(*creator)()
是函数的调用。
要完成此操作,我们需要在 map 中确实包含一些内容...因此我们将其添加到构造函数中:
class OperationFactory {
public:
....
OperationFactory() {
mEncoders[OT_MY]=&MyEncoder::create;
mEncoders[OT_MY]=&MyDecoder::create;
mEncoders[OT_OTHER]=&OtherEncoder::create;
mEncoders[OT_OTHER]=&OtherDecoder::create;
}
....
};
我们为每种算法插入指向其
create
静态方法的指针。
最后,我们如何使用它?
int main(int argc,char **argv) {
OperationFactory f;
Operation *o=f.getOperation(OperationFactory::OD_DECODER,OperationFactory::OT_MY);
std::string toTransform="Hello world";
std::string transformed=(*o)(toTransform);
delete o; // don't forget to delete it.
}
在这里,我们有一个
OperationFactory
f
的实例,从中我们可以使用
getOperation()
方法请求创建所需的操作。
我们获得的对象可用于执行算法。请注意,
(*o)(toTransform)
在形式上与我们上面创建者的调用类似,但有一些区别:
o
是指向Operation
类型的对象的指针(实际上实际上是一个指针MyEncoder
)
(*o) is an object of type
操作(well, really of type
MyEnconder`)
(*o)(toTransform)
是operator()
类型的MyEncoder
方法的调用。
我们本可以在Creator上使用此技术:使用对象函数而不是函数的指针...但是可能会需要更多代码。
请注意,工厂分配了内存...,并且在不再需要此内存时必须将其处置。不这样做的方法是使用unique_ptr或shared_ptr ...
请注意,当
getOperation()
找不到所请求的算法时,它可能返回空指针...因此,调用代码应检查这种可能性。
替代地,
getOperation()
的实现可以选择在找不到算法时引发异常...再次,调用代码应具有try / catch。
现在,如何添加新算法:
从操作派生并实现您的编码器和解码器类
展开枚举OperationType
在OperationFactory构造函数中注册创建者。
...使用它。
我是一名优秀的程序员,十分优秀!