【问题标题】:How do I access a pointer's subclass's methods if those methods are unique to the subclass?如何访问一个指针子类的方法那些方法是子类独有的?
【发布时间】:2019-09-25 21:47:41
【问题描述】:

我的程序的一部分有两种可能的情况:(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;

【问题讨论】:

  • 您可能希望将它们包装在单独的类中,您可以在其中调用 openclose,而没有 ifstream 的类将什么都不做,而另一个类实际上会打开和关闭ifstream。虽然你对dynamic_cast 有正确的想法,但你应该getif (ifstream* filestream = dynamic_cast...) { filestream-&gt;close() 这样的回报
  • 我开始写答案,但后来我意识到,导致您的问题的那些行:没有必要。如果您将值传递给std::ifstream 的构造函数 - 它会自行调用open。并且调用close 是不必要的,因为std::ifstream 的析构函数会调用它。这留下了一个真正的问题:delete currentStream; 是未定义的行为,当argc != 3 时,由于它试图delete &amp;cin

标签: c++ pointers subclass c++-standard-library


【解决方案1】:

通过抓取缓冲区动态分配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;
  }
}

【讨论】:

    【解决方案2】:

    有几个地方可以改进您的代码。我认为最突出的改进地方是你试图在一个功能上做太多事情。您不复制代码的目标是好的,但要模块化您的方法。将共享代码移动到自己的函数中,如:

    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;
        }
    }
    

    此函数应包含两个代码路径之间共享的所有内容。 (我更改了指向引用的指针,以便调用者立即知道空指针是不可接受的。)当您更改 main 函数以便它调用这个时,您应该注意到一些事情变得更容易了。特别是,不需要动态分配(这导致没有尝试delete &amp;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 是否需要关闭,因为您从未离开创建文件的代码分支。


    还有一个地方可以简化事情。不要打电话给openclose。您正在使用带有文件名的ifstream 构造函数,因此构造函数已经为您调用了open。 (当您显式调用open 时,您是在告诉计算机关闭文件并重新-打开它。)同样,析构函数将为您调用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;
    }
    

    【讨论】:

      【解决方案3】:

      简单地提取一个方法:

      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);
          }
      }
      

      【讨论】:

        【解决方案4】:

        Tas 在 cmets 中有正确的方法。不能直接在 currentStream 上调用该方法,必须在 cast 接口上调用。

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

        我还认为您可能应该将代码更改为不依赖动态转换,无论是新接口还是单独的方法。

        【讨论】:

        • 1) 但是,甚至不需要调用close,如果一个是delete 分配的流-std::ifstream 的析构函数会调用它。 2)从技术上讲,提问者也询问了currentStream-&gt;open(argv[2]); 行(此答案未解决),但这也是多余的,因为使用了构造函数。 3)这个答案没有提到delete currentStream;的可能UB。
        • 我认为您在if 条件和调用close 的行中的意思是“流”而不是“ifstream”?
        猜你喜欢
        • 1970-01-01
        • 2017-04-13
        • 1970-01-01
        • 1970-01-01
        • 2017-07-15
        • 2012-05-27
        • 1970-01-01
        • 1970-01-01
        • 2021-07-09
        相关资源
        最近更新 更多