Как получить доступ к методам подкласса указателя, если эти методы уникальны для подкласса? - PullRequest
1 голос
/ 26 сентября 2019

Часть моей программы имеет два возможных случая: (1) если пользователь дает только 2 аргумента командной строки, получает ввод из стандартного ввода (cin) (2), если пользователь дает 3 аргумента командной строки (последний из которых являетсяимя файла), принять входные данные из файла.Чтобы не использовать один и тот же код для обеих опций, я попытался использовать указатель на суперкласс cin и ifstream, istream для обоих способов ввода.

Моя проблема в том, что в строках 5 и 22 приведенного ниже кода я пытаюсь ссылаться на методы, доступные только для подкласса ifstream (открывать и закрывать).Исходя из моей логики, если эти методы вызываются, указатель должен указывать на тип ifstream, но программа не может скомпилироваться, потому что эти методы не определены в классе istream.

Есть ли способобойти это?

istream *currentStream;
    if (argc == 3) {
        // Handle optional file input
        currentStream = new ifstream(argv[2]);
        currentStream->open(argv[2]);
        if (currentStream->fail()) {
            cerr << "FILE COULD NOT BE OPENED\n";
            return 1;
        }
    } else {
        currentStream = &cin;
    }
    string myLine;
    // go line by line and translate it
    while (getline(*currentStream, myLine)) {
        if (currentStream->eof()) {
            break;
        }
        cout << rot13(myLine) << endl;
    }
    if (dynamic_cast<ifstream*>(currentStream)) {
        currentStream->close();
    }
    // handle pointer
    delete currentStream;
    currentStream = NULL;
    return 0;

Ответы [ 4 ]

3 голосов
/ 26 сентября 2019

Динамически выделить «копию» std::cin, захватив ее буфер.Хранение памяти в std::unique_ptr также было бы идеальным, поскольку вам не пришлось бы беспокоиться об удалении указателя вручную.

#include <memory>

int main(int argc, char* argv[]) {
  std::unique_ptr<std::istream> currentStream( argc == 3
    ? std::make_unique<std::ifstream>(argv[2])
    : std::make_unique<std::istream>(std::cin.rdbuf())
  );

  // will only fail when the file cannot open
  if (!currentStream) {
    std::cerr << "FILE COULD NOT BE OPENED\n";
    return 1;
  }

  std::string myLine;
  // go line by line and translate it
  while (std::getline(*currentStream, myLine)) {
    std::cout << rot13(myLine) << std::endl;
  }
}
1 голос
/ 26 сентября 2019

Есть несколько мест, где ваш код может быть улучшен.Я думаю, что наиболее заметным местом для улучшения является то, что вы пытаетесь сделать слишком много в одной функции.Ваша цель не дублировать код - это хорошо, но модульный подход.Переместите общий код в его собственную функцию, например:

void do_stuff(std::istream & currentStream)
{
    std::string myLine;
    // go line by line and translate it
    while (getline(currentStream, myLine)) {
        if (currentStream.eof()) {
            break;
        }
        std::cout << rot13(myLine) << std::endl;
    }
}

Эта функция должна содержать все, что совместно используется двумя путями кода.(Я изменил указатель на ссылку, чтобы вызывающие сразу знали, что нулевой указатель недопустим.) Когда вы меняете основную функцию так, чтобы она вызывала эту, вы должны заметить, что некоторые вещи становятся проще.В частности, нет необходимости в динамическом размещении (что не приводит к попытке delete &cin - что выглядело плохо).Вы можете легко использовать локальную переменную для своего файлового потока.

int main(int argc, const char ** argv)
{
    if (argc == 3) {
        // Handle optional file input
        std::ifstream fileStream(argv[2]);
        fileStream.open(argv[2]);
        if (fileStream.fail()) {
            std::cerr << "FILE COULD NOT BE OPENED\n";
            return 1;
        }
        do_stuff(fileStream);
        fileStream.close();
    } else {
        do_stuff(std::cin);
    }
    return 0;
}

Перемещая общий код в отдельную функцию, вы получаете возможность остаться в предложении if.Нет необходимости определять, нужно ли закрывать *currentStream, так как вы никогда не покидали ветвь кода, создавшую файл.


Есть еще одно место, где вы могли бы упростить вещи.Не звоните open и close.Вы используете конструктор ifstream, который принимает имя файла, поэтому конструктор уже вызывает для вас open.(Когда вы явно вызываете open, вы говорите компьютеру закрыть файл и re - открыть его.) Точно так же деструктор вызовет для вас close;это ключевой момент RAII .

. Избавление от ненужных вызовов оставляет:

int main(int argc, const char ** argv)
{
    if (argc == 3) {
        // Handle optional file input
        std::ifstream fileStream(argv[2]);
        if (fileStream.fail()) {
            std::cerr << "FILE COULD NOT BE OPENED\n";
            return 1;
        }
        do_stuff(fileStream);
        // Keep in mind that, even though there is no C++ code here, there is something
        // important being done after the call to do_stuff. Specifically, the destructor
        // for fileStream is called, which closes the file for you.
    } else {
        do_stuff(std::cin);
    }
    return 0;
}
0 голосов
/ 26 сентября 2019

Просто извлеките метод:

void process(std::istream is) {
    string myLine;
    // go line by line and translate it
    while (getline(is, myLine))
        cout << rot13(myLine) << endl;
}

int main(int argc, char** argv) {
    if (argc == 3) {
        ifstream ifs(argv[2]);
        if (!ifs) {
            cerr << "FILE COULD NOT BE OPENED\n";
            return 1;
        }
        process(ifs);
    } else {
        process(cin);
    }
}
0 голосов
/ 26 сентября 2019

Тас имеет правильный подход в комментариях.Вы не можете вызывать метод непосредственно в currentStream, вы должны вызывать его в интерфейсе приведения.

ifstream* stream = dynamic_cast<ifstream*>(currentStream);
if (stream) {
     stream->close();
}

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

...