Une partie de mon programme a deux cas possibles: (1) si l'utilisateur ne donne que 2 arguments de ligne de commande, prendre l'entrée de l'entrée standard (cin) (2) si l'utilisateur donne 3 arguments de ligne de commande (le dernier étant un nom de fichier), prenez l'entrée du fichier. Afin de ne pas réutiliser le même code pour les deux options, j'ai essayé d'utiliser un pointeur vers une superclasse à la fois cin et ifstream, istream pour les deux moyens d'entrée.
Mon problème est que sur les lignes 5 et 22 du code ci-dessous, j'essaie de référencer les méthodes disponibles uniquement pour la sous-classe ifstream (ouvrir et fermer). D'après ma logique, si ces méthodes sont appelées, le pointeur doit pointer vers un type ifstream, mais le programme ne parvient pas à se compiler car ces méthodes ne sont pas définies dans la classe istream.
Y a-t-il un moyen pour contourner ce problème?
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 Réponses :
Tas a la bonne approche dans les commentaires. Vous ne pouvez pas appeler la méthode directement sur currentStream, vous devez l'appeler sur l'interface de cast.
ifstream* stream = dynamic_cast<ifstream*>(currentStream); if (stream) { stream->close(); }
Je pense aussi que vous devriez probablement changer votre code pour ne pas compter sur le cast dynamique, soit un nouvelle interface ou méthode distincte.
1) Mais, on n'a même pas besoin d'appeler close
, si l'on est delete
ing le flux alloué - destructeur de std :: ifstream
va l'appeler. 2) Techniquement, le demandeur a posé une question sur la ligne currentStream-> open (argv [2]);
également (qui n'est pas traitée avec cette réponse), mais elle aussi est superflue, car le constructeur est utilisé. 3) Cette réponse ne mentionne pas la possible UB de delete currentStream;
.
Je pense que vous vouliez dire « flux » au lieu de « ifstream » dans votre si code> condition et dans la ligne qui appelle
Fermer code>?
Allouez dynamiquement une "copie" de std :: cin
en saisissant son tampon. Le stockage de la mémoire dans un std :: unique_ptr
serait également idéal car vous n'auriez pas à vous soucier de la suppression manuelle du pointeur.
#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; } }
Votre code peut être amélioré à plusieurs endroits. Je pense que le point le plus important pour l'amélioration est que vous essayez d'en faire trop dans une fonction. Votre objectif de ne pas dupliquer le code est bon, mais modulez votre approche. Déplacez le code partagé vers sa propre fonction, comme dans:
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; }
Cette fonction doit contenir tout ce qui est partagé entre les deux chemins de code. (J'ai changé le pointeur en référence pour que les appelants sachent immédiatement qu'un pointeur nul n'est pas acceptable.) Lorsque vous modifiez votre fonction principale pour qu'elle appelle celle-ci, vous devriez remarquer que certaines choses deviennent plus faciles. En particulier, il n'y a pas besoin d'allocation dynamique (ce qui n'entraîne aucune tentative de supprimer & cin
- cela avait l'air mauvais). Vous pouvez facilement utiliser une variable locale pour votre flux de fichiers.
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; }
En déplaçant le code commun vers une fonction distincte, vous restez dans votre if code > clause. Il n'est pas nécessaire de déduire si
* currentStream
doit être fermé ou non, puisque vous n'avez jamais quitté la branche de code qui a créé le fichier.
Il y a un autre endroit où vous pourriez simplifier les choses. N'appelez pas Se débarrasser des appels inutiles laisse: open
et close
. Vous utilisez le constructeur ifstream
qui prend un nom de fichier, donc le constructeur appelle déjà open
pour vous. (Lorsque vous appelez explicitement open
, vous dites à l'ordinateur de fermer le fichier et de le rouvrir .) De même, le destructeur appellera close code > pour vous; c'est un point clé de RAII . p>
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;
}
}
Extrayez simplement une méthode:
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); } }
Vous voudrez probablement les envelopper dans des classes séparées, où vous pourriez appeler
open
etclose
, et la classe sansifstream
ne ferait simplement rien , et l'autre classe ouvrirait et fermerait en fait leifstream
. Vous avez la bonne idée avecdynamic_cast
, mais vous devriez obtenir le retour commeif (ifstream * filestream = dynamic_cast ...) {filestream-> close ( )
J'ai commencé à écrire la réponse, mais ensuite j'ai réalisé que ces lignes, qui causent vos problèmes: ne sont pas nécessaires. Si vous passez la valeur au constructeur de
std :: ifstream
- il appelleraopen
tout seul. Et l'appel àclose
n'est pas nécessaire, car le destructeur, destd :: ifstream
, l'appellera. Cela laisse avec le seul, vrai, problème:delete currentStream;
est un comportement indéfini, lorsqueargc! = 3
, car il essaie desupprimer & cin code >.