好的,所以我尝试过实现简单的单字母替换密码,如Caesars,digraph like playfair,polyalphabetic,如autokey,vigenre和其他一些c ++ {不使用类}。现在我想将所有这些密码和其他几个密码汇集在一起并将其打包成一个项目。我已经开始编写几行代码,但我不确定我必须如何设计它。这是我的课程的样子。
我的前端 //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接口
//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 - &gt; 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.
}
这是一个非常粗略的例子。这里只介绍课堂设计。这里有一些疑点。
实现:我接受来自用户的字符串,然后将其传递给类atbash的构造函数,然后将其复制到从基类密码继承的字符串数据成员plaintxt。然后我会从构造函数调用函数制表。现在我有两个选择:tabulate()生成加密表的哈希映射并将其存储在Etable中,或者它也可以生成解密表。在atbash密码的情况下这些是一个但是一样的。但是一般的单字母替换密码的情况呢?我将如何强制制表功能来创建任何一个。
我的想法是将构造函数的字符参数传递给构造函数,该构造函数描述给定字符串是否要加密或解密,并相应地将其保存在plaintxt或ciphertxt中的任何一个中。此外,构造函数将此字符参数传递给制表表相应地将加密或解密表制成表格的功能。这样好吗?
关于如何改进这个的任何建议?
接口:我从main.cpp实现所有这些密码的接口的方法就是使用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";
.....................
.
.....
}
有没有更好的方法可以在不使用switch case的情况下实现它。
同样你可以看到我已经使用了一个指向派生类对象的基类指针来执行此操作。我知道这里没有必要,我可以简单地继续声明一个对象。通过基类指针引用对象有什么重要意义吗? 我听说这些基类指针有时候可以成为真正的救世主!如果是这样,请指导我简化编码的场景。在这个特定的情况下,在基类中声明纯虚函数并没有任何用处。这只是膨胀代码吗?
我应该继续将类实现分离成单独的文件,就像我在这里完成的那样,或者我应该在一个main.cpp中将所有这些代码捏起来,这样可以使继承变得更容易,因为你没有使用头文件。
请指导我。我没有专业的编码经验,我很乐意在这里提出您的意见。
答案 0 :(得分:3)
一些想法,没有特别的顺序
这里有许多设计决策,许多权衡取决于不同的事情。
希望以上几行能给你一些启发。
我们将从Operation
课开始。这假设我们可以使用相同的API调用加密器和解密器
class Operation {
public:
virtual ~Operation() { }
virtual std::string operator()(const std::string &input)=0;
virtual void reset() { }
};
注意事项:
operator()
纯虚方法。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()
的实现上
你可以:
现在为工厂:
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;
};
下面:
Operation
的指针。静态方法与函数相同(至少关于函数指针)...所以这个typedef允许我们为上面的神秘create()
静态方法命名。OperatonType
到Creator
函数的地图。有了这个,我们可以为用户提供一些方法来获得它想要的东西:
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)()
是函数的调用。要完成此操作,我们需要在地图中确实有一些内容......所以我们在构造函数中添加它:
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
{操作{1}} MyEnconder`)(well, really of type
是(*o)(toTransform)
类型的operator()
方法的调用。我们可以在Creator上使用这种技术:使用对象函数而不是指向函数的指针......但是它会有更多的代码。
请注意,工厂分配内存......并且必须在不再需要时处理此内存。不这样做的方法是使用unique_ptr或shared_ptr ......
请注意,MyEncoder
在找不到请求的算法时可能会返回空指针...因此调用代码应检查该可能性。
或者getOperation()
的实现可能选择在找不到算法时抛出异常...再次调用代码应该有一个try / catch。
现在,如何添加新算法: