Генератор паролей

Посетила меня идея создать генератор паролей при помощи класса.
Файл main.cpp:

#include <iostream>

using namespace std;

#include "KeyGen.cpp"

int main()
{
    srand ( time(NULL) );
    KeyGen k;
    k.generate(15, gen_mode::with_alfa | gen_mode::with_up_alfa | gen_mode::with_number | gen_mode::with_simbols );
    cout << k.pass();
    return 0;
}

Файл KeyGen.h:

//#include <iostream>

namespace gen_mode
{
    __int8 with_alfa = 1; // bit #0
    __int8 without_alfa = 2; // bit #1
    __int8 with_number = 4; // bit #2
    __int8 without_number = 8; // bit #3
    __int8 with_up_alfa = 16; // bit #4
    __int8 without_up_alfa = 32; // bit #5
    __int8 with_simbols = 64; // bit #6
    __int8 without_simbols = 128; // bit #7
}


class KeyGen
{
    private:
        char *_pass;
        bool gen;
    public:
        KeyGen();
        void generate ( int len, __int8 genmode );
        char* pass();
        //friend ostream &operator<< ( ostream &stream, KeyGen &ob );
        ~KeyGen();
};

Файл KeyGen.cpp:

#include <cstdlib>
#include <ctime>

#include "KeyGen.h"

int _strlen ( const char *str )
{
    int len;
    for ( len = 0; str[len]; len++ ) ;
    return len;
}


template <typename type> 
void setbit ( type &i, int position )
{
    i |= ( 1 << (position) );
    return;
}

template <typename type>
int get_bit ( type i, int position )
{
    type temp = 0;
    setbit ( temp, position );
    i &= temp;
    return i;
}

KeyGen :: KeyGen () : gen(false)
{
}

void KeyGen :: generate ( int len, __int8 genmode )
{
    if ( gen ) delete []_pass;
    char alfa[] = "qwertyuiopasdfghjklzxcvbnm";
    char upalfa[] = "QWERTYUIOPASDFGHJKLZXCVBNM";
    char simbols[] = "@#$%^&*()_-+={}[];:'\".>,</?|~! ";
    char numbers[] = "1234567890";
    gen = true;
    _pass = new char[len];
    int temp, index, i;
    bool bad = false;
    for ( i = 0; i < len; i++ )
    {
        if ( bad ) i--;
        bad = false;
        temp = rand() % 4;
        switch ( temp )
        {
            case 0:
                {
                    if ( !get_bit ( genmode, 0 ) ) bad = true;
                    else 
                    {
                        index = rand() % _strlen(alfa)+1;
                        _pass[i] = alfa[index];
                    }
                };break;
            case 1:
                {
                    if ( !get_bit ( genmode, 2 ) ) bad = true;
                    else
                    {
                        index = rand() % _strlen(numbers)+1;
                        _pass[i] = numbers[index];
                    }
                };break;
            case 2:
                {
                    if ( !get_bit ( genmode, 4 ) ) bad = true;
                    else
                    {
                        index = rand() % _strlen(upalfa)+1;
                        _pass[i] = upalfa[index];
                    }
                };break;
            case 3:
                {
                    if ( !get_bit ( genmode, 6 ) ) bad = true;
                    else
                    {
                        index = rand() % _strlen(simbols)+1;
                        _pass[i] = simbols[index];
                    }
                };break;
            default:
                {
                    abort();
                };break;
        }
    }
}

char* KeyGen :: pass()
{
    if ( gen ) return _pass;
    else return NULL;
}

/*ostream &operator<< ( ostream &stream, KeyGen &ob )
{
    stream << ob._pass;
    return stream;
}*/

KeyGen :: ~KeyGen ()
{
    if ( gen ) delete []_pass;
}

Вроде ничего ошибочного нет. Но не компилируется. С ошибкой:
KeyGen.cpp:(.text+0x0): multiple definition of_strlen(char const*)'` и так на каждый метод класса. В чём проблема?

Не изобретай велосипед. Функция вычисления длины строки с концевым нулём есть в стандартной библиотеке.

Кроме того, ты слишком перемудрил с кодом. Придерживайся принципа KISS.

Кроме того, обнаружилось, что ты забыл про концевой ноль в строке пароля.

И опять cpp-файл в #include ((

Я слил всё в один файл и немного соптимизировал.


#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>

using namespace std;

namespace gen_mode
{
    const int ALPHA     = 1; // bit #0
    const int UALPHA    = 2; // bit #1
    const int NUMBER    = 4; // bit #2
    const int SYMBOL    = 8; // bit #3

    const string alpha   = "qwertyuiopasdfghjklzxcvbnm";
    const string ualpha  = "QWERTYUIOPASDFGHJKLZXCVBNM";
    const string numbers = "1234567890";
    const string symbols = "@#$%^&*()_-+={}[];:'\".>,</?|~! ";
}

class KeyGen
{
    private:
        char *_pass;
    public:
        KeyGen();
        const char *generate ( int len, int genmode );
        const char* pass() const;
        //friend ostream &operator<< ( ostream &stream, const KeyGen &ob );
        ~KeyGen();
};


KeyGen::KeyGen () : _pass(NULL) {}



const char *KeyGen::generate ( int len, int genmode )
{
    if (_pass != NULL) 
        delete [] _pass;

    _pass = new char[len + 1];          // нужно место для завершающего \0

    string symset = "";

    if (genmode & gen_mode::ALPHA) {
        symset += gen_mode::alpha;
    }
    if (genmode & gen_mode::UALPHA) {
        symset += gen_mode::ualpha;
    }
    if (genmode & gen_mode::NUMBER) {
        symset += gen_mode::numbers;
    }
    if (genmode & gen_mode::SYMBOL) {
        symset += gen_mode::symbols;
    }

    size_t max_index = symset.length();

    for (int i = 0; i < len; i++ ) {
        _pass[i] = symset[rand() % max_index];
    }
    _pass[len] = '\0';                   // добавляем завершающий \0

    return _pass;
}

const char* KeyGen :: pass() const
{
    return _pass;
}

/*ostream &operator<< ( ostream &stream, const KeyGen &ob )
{
    stream << ob._pass;
    return stream;
}*/

KeyGen :: ~KeyGen ()
{
    if (_pass != NULL) 
        delete [] _pass;
}


int main()
{
    srand ( time(NULL) );
    KeyGen k;
    cout << k.generate(15, gen_mode::ALPHA | gen_mode::UALPHA | gen_mode::NUMBER | gen_mode::SYMBOL) << endl;
    cout << "Method pass: " << k.pass() << endl;
    cout << "Only ALPHA : " << k.generate(15, gen_mode::ALPHA) << endl;
    cout << "Only UALPHA: " << k.generate(15, gen_mode::UALPHA) << endl;
    cout << "Only NUMBER: " << k.generate(15, gen_mode::NUMBER) << endl;
    cout << "Only SYMBOL: " << k.generate(15, gen_mode::SYMBOL) << endl;

    system("pause");
    return 0;
}

А вообще-то говоря, класс здесь не нужен. Достаточно было бы функции, которая генерировала бы пароль в буфер, предоставляемый вызывающим кодом. Что-то типа такого:

void passgen(char *buf, unsigned len, unsigned genmode);

Череп, может я чего-то не понимаю, но если я включу в файл main.cpp только файл KeyGen.h, то откуда компилятор возьмёт реализацию методов, если она есть только в файле KeyGen.cpp, который не включен в файл main.cpp?

Я попытался разбить ваш код на файлы(правильно или нет?):

Файл main.cpp:


#include <iostream>
#include <ctime>
#include <cstdlib>


using namespace std;

#include "KeyGen.h"

int main()
{
    srand ( time(NULL) );
    KeyGen k;
    cout << k.generate(15, gen_mode::ALPHA | gen_mode::UALPHA | gen_mode::NUMBER | gen_mode::SYMBOL) << endl;
    cout << "Method pass: " << k.pass() << endl;
    cout << "Only ALPHA : " << k.generate(15, gen_mode::ALPHA) << endl;
    cout << "Only UALPHA: " << k.generate(15, gen_mode::UALPHA) << endl;
    cout << "Only NUMBER: " << k.generate(15, gen_mode::NUMBER) << endl;
    cout << "Only SYMBOL: " << k.generate(15, gen_mode::SYMBOL) << endl;

    system("pause");
    return 0;
}

Файл KeyGen.h:

#include <string>

namespace gen_mode
{
    const int ALPHA     = 1; // bit #0
    const int UALPHA    = 2; // bit #1
    const int NUMBER    = 4; // bit #2
    const int SYMBOL    = 8; // bit #3

    const string alpha   = "qwertyuiopasdfghjklzxcvbnm";
    const string ualpha  = "QWERTYUIOPASDFGHJKLZXCVBNM";
    const string numbers = "1234567890";
    const string symbols = "@#$%^&*()_-+={}[];:'\".>,</?|~! ";
}

class KeyGen
{
    private:
        char *_pass;
    public:
        KeyGen();
        const char *generate ( int len, int genmode );
        const char* pass() const;
        //friend ostream &operator<< ( ostream &stream, const KeyGen &ob );
        ~KeyGen();
};

Файл KeyGen.cpp:


#include "KeyGen.h"

#include <cstdlib>
#include <ctime>
#include <string>

KeyGen::KeyGen () : _pass(NULL) {}

const char *KeyGen::generate ( int len, int genmode )
{
    if (_pass != NULL) 
        delete [] _pass;

    _pass = new char[len + 1];          // íóæíî ìåñòî äëÿ çàâåðøàþùåãî \0

    string symset = "";

    if (genmode & gen_mode::ALPHA) {
        symset += gen_mode::alpha;
    }
    if (genmode & gen_mode::UALPHA) {
        symset += gen_mode::ualpha;
    }
    if (genmode & gen_mode::NUMBER) {
        symset += gen_mode::numbers;
    }
    if (genmode & gen_mode::SYMBOL) {
        symset += gen_mode::symbols;
    }

    size_t max_index = symset.length();

    for (int i = 0; i < len; i++ ) {
        _pass[i] = symset[rand() % max_index];
    }
    _pass[len] = '\0';                   // äîáàâëÿåì çàâåðøàþùèé \0

    return _pass;
}

const char* KeyGen :: pass() const
{
    return _pass;
}

/*ostream &operator<< ( ostream &stream, const KeyGen &ob )
{
    stream << ob._pass;
    return stream;
}*/

KeyGen :: ~KeyGen ()
{
    if (_pass != NULL) 
        delete [] _pass;
}

И тут появилась ошибка:
'string' does not name a type к файлу KeyGen.h
и 'string' was not declared in this scope к файлу KeyGen.cpp. В чём прикол?

Я не знаю каким компилятором и IDE ты пользуешься, поэтому не могу тебе ответить точно на первый вопрос.

Если ты пользуешься IDE, то в них существует понятие «проекта». В проекте указываются все файлы исходного кода, ресурсы и т.п., зависимости между ними, а также порядок «изготовления» выходного файла (это может быть исполняемый модуль, библиотека, динамическая библиотека и т.п.): порядок вызовов компилятора, линкера, библиотекаря и пр., а также параметры их вызовов. В этом случае тебе надо добавить в проект все три файла.

Если ты работаешь по старинке, вызывая компилятор из командной строки, то тебе сначала надо все cpp-файлы откомпилировать в объектные файлы, а затем линкером всё собрать в один exe'шник.

Код разбил на файлы в общем-то правильно. Единственно, в h-файл надо добавить защиту от множественного включения, #include <iostream> и включение пространства имён std для string и ostream:

#ifndef _KEYGEN_H_
#define _KEYGEN_H_

#include <iostream>
#include <string>

using namespace std;

namespace gen_mode
{
    const int ALPHA     = 1; // bit #0
    const int UALPHA    = 2; // bit #1
    const int NUMBER    = 4; // bit #2
    const int SYMBOL    = 8; // bit #3

    const string alpha   = "qwertyuiopasdfghjklzxcvbnm";
    const string ualpha  = "QWERTYUIOPASDFGHJKLZXCVBNM";
    const string numbers = "1234567890";
    const string symbols = "@#$%^&*()_-+={}[];:'\".>,</?|~! ";
}

class KeyGen
{
    private:
        char *_pass;
    public:
        KeyGen();
        const char *generate ( int len, int genmode );
        const char* pass() const;
        //friend ostream &operator<< ( ostream &stream, const KeyGen &ob );
        ~KeyGen();
};
#endif

По поводу ошибок, которые ты получил: это скорее всего от того, что не включено пространство имён std.

Я сейчас проверил твою версию моего кода (с вышеуказанными правками) под Dev-C++ 5.5.3 — всё компилится и собирается нормально.

Что то я не понял, то есть если я создаю проект(DevCpp 5.4.2, все три файла в проекте), то все связи между файлами устанавливает линкер? и мне не надо
их соединять с помощью #include?
И ещё, не могли бы вы мне объяснить, немного, как работает защита от множественного включения(и почему её нет в файле KeyGen.cpp?) или кинуть ссылку где про это говорится, а то по запросу C++ защита от множественного включения я ничего не нашёл...

Череп, selevit, спасибо, и извините, сильно тупил, а вы как всегда всё «разжевали» и «разложили по полочкам».

Да, IDE сама откомпилирует и свяжет все файлы проекта.

Директива препроцессора #include — это просто вставка содержимого файла, указанного в директиве, вместо самой директивы на уровне текста. Это происходит до компиляции.

Поэтому у тебя директива #include "KeyGen.h" должна быть в файлах main.cpp и KeyGen.h, что бы объявить класс KeyGen. Откомпилированное определение (реализация) класса будет подтянуто линкером.

Защита от множественного включения работает до безобразия просто. Вот она:

#ifndef _KEYGEN_H_
#define _KEYGEN_H_

...

#endif

У препроцессора имеется таблица символов, куда он заносит все символы, определённые директивами #define и определённые в командной строке препроцессора (компилятора).

Когда файл KeyGen.h попадается препроцессору в первый раз, происходит следующее. Первая директива — это условная компиляция: если указанный символ не определён (IF Not DEFined). Символ _KEYGEN_H_ не определён, условие выполняется, будет обрабатываться следующая строка текста. Вторая директива — определение символа _KEYGEN_H_. Далее идёт текст, который препроцессор выводит в выходной поток (для компиляции). Директива #endif закрывает блок условной компиляции.

Когда файл KeyGen.h попадается препроцессору в следующий раз, символ _KEYGEN_H_ будет уже определён. Поэтому условие в первой директиве не выполнится и весь текст до директивы #endif включительно не будет выведен в выходной поток и не будет откомпилирован.

Это классический случай.

Есть альтернативный способ: директива компилятора

#pragma once

Но это, что называется, «особенности реализации», зависящие от компилятора. А директивы препроцессора работают везде.

Рекомендую почитать в книжке про директивы препроцессора.

Извиняюсь, недоглядел. Файл KeyGen.h должен выглядеть так:

#ifndef _KEYGEN_H_
#define _KEYGEN_H_

#include <iostream>
#include <string>

using namespace std;

namespace gen_mode
{
    extern const int ALPHA;
    extern const int UALPHA;
    extern const int NUMBER;
    extern const int SYMBOL;

    extern const string alpha;
    extern const string ualpha;
    extern const string numbers;
    extern const string symbols;
}

class KeyGen
{
    private:
        char *_pass;
    public:
        KeyGen();
        const char *generate ( int len, int genmode );
        const char* pass() const;
        //friend ostream &operator<< ( ostream &stream, const KeyGen &ob );
        ~KeyGen();
};
#endif

а определение констант должно быть перенесено в файл KeyGen.cpp:

#include "KeyGen.h"

#include <cstdlib>
#include <ctime>
#include <string>


namespace gen_mode
{
    const int ALPHA     = 1; // bit #0
    const int UALPHA    = 2; // bit #1
    const int NUMBER    = 4; // bit #2
    const int SYMBOL    = 8; // bit #3

    const string alpha   = "qwertyuiopasdfghjklzxcvbnm";
    const string ualpha  = "QWERTYUIOPASDFGHJKLZXCVBNM";
    const string numbers = "1234567890";
    const string symbols = "@#$%^&*()_-+={}[];:'\".>,</?|~! ";
}


KeyGen::KeyGen () : _pass(NULL) {}

const char *KeyGen::generate ( int len, int genmode )
{
...

Почему-то компилятор не счёл это ошибкой и даже не выдал предупреждения. Хотя при включении файла KeyGen.h в KeyGen.cpp и main.cpp должно было получиться два набора констант и при линковке должны были появиться ошибки о переопределении символов. Чудеса однако ((

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.