【发布时间】:2018-11-23 20:40:45
【问题描述】:
在 test.dat(输入文件)中给出的数据是:
1 3.3 Langestrasse 2.200000
0 4.4 Koeningsstrasse
0 5.5 Koeniginstrasse
以下程序应该(将数据解析为 Tankstelle / TankstelleDeLuxe 类的实例并)将数据以相反的顺序保存在 protokoll.dat
但是Windows 10下写的输出(g++ --version:g++ (x86_64-win32-seh-rev1, Built by MinGW-W64 project) 7.2.0的输出)
进入 Protokoll.dat 是:
0 5.5
0 4.4 Koeningsstrasse
1 3.3 Langestrasse 2.200000
“Koeniginstrasse”属性明显丢失。
有趣的是,据我的朋友(程序作者,我有权问这个问题)说,在他的Linux环境下不会发生这种行为。
tl;dr:尽管 Makefile 和编译数据相同,但无法保证可执行文件的正确性。
有趣的是,据我朋友(程序的作者,我有权问这个问题)说,在他的Linux环境下不会出现这种错误行为。
我无法制作 MWE。甚至列表中第 7 步之后的调试输出似乎也没有问题。该程序本身当然不是最优雅的解决方案,它是为完成家庭作业而设计的。
重现步骤:
- 将所有给定文件保存在同一文件夹中
- 在此文件夹中运行 make。
- 运行 ./main.exe
- 用 3 回答
- 用J回答
- 用 test.dat 回答
- 用J回答
- 查看 protokoll.dat。是否插入了“Koeniginstrasse”?
如果你能给我一些提示,我会很高兴
- 这种奇怪的行为是否也发生在您的 PC 上
- 为什么 Koeniginstrasse 没有保存到 protokoll.dat 中
关键行似乎是 main.cpp 中的第 126 行。
f << meineListe.at(i)->benzinpreisAusgabe() << " " << meineListe.at(i)->adresseAusgabe() << " ";
非常感谢!
文件: 生成文件:
main: main.o tankstelle.o tankstelleDeLuxe.o
g++ -std=c++11 main.o tankstelle.o tankstelleDeLuxe.o -o main
main.o: tankstelle.h tankstelleDeLuxe.h main.cpp
g++ -std=c++11 -c main.cpp
tankstelle.o: tankstelle.cpp tankstelle.h
g++ -std=c++11 -c tankstelle.cpp
tankstelleDeLuxe.o: tankstelle.h tankstelleDeLuxe.cpp tankstelleDeLuxe.h
g++ -std=c++11 -c tankstelleDeLuxe.cpp
clean:
rm main *.o
tankstelle.cpp:
#include <iostream>
#include "tankstelle.h"
//#include <unistd.h>
//#include <stdlib.h>
//#include <cstdlib>
//Initialisierung:
Tankstelle::Tankstelle(std::string adresse, double benzinpreis,bool waschstrasse)
: adresse(adresse), benzinpreis(benzinpreis), waschstrasse(waschstrasse)
{};
double Tankstelle::benzinpreisAusgabe(){
return this->benzinpreis;
}
std::string Tankstelle::adresseAusgabe(){
return this->adresse;
}
bool Tankstelle::waschstrasseAusgabe(){
return this->waschstrasse;
}
tankstelle.h:
//tankstelle.h
#ifndef _TANKSTELLE_H_
#define _TANKSTELLE_H_
#include<string> //fuer Adresse
class Tankstelle
{
public: // oeffentlich
Tankstelle(std::string adresse="Unbekannt", double benzinpreis=0,bool waschstrasse = false ); // // Konstruktor mit Parameter und Defaultwert
double benzinpreisAusgabe();
std::string adresseAusgabe();
bool waschstrasseAusgabe();
bool waschstrasse;
protected: // privat
std::string adresse;
double benzinpreis;
};
#endif //_TANKSTELLE_H_
tankstelleDeLuxe.cpp:
#include "tankstelle.h"
#include "tankstelleDeLuxe.h"
TankstelleDeLuxe::TankstelleDeLuxe(std::string adresse, double benzinpreis, double waeschepreis)
: Tankstelle(adresse, benzinpreis, true), waeschepreis(waeschepreis)
{}
void TankstelleDeLuxe::waeschepreisEingabe(double waeschepreis){
this->waeschepreis = waeschepreis;
}
double TankstelleDeLuxe::waeschepreisAusgabe(){
return this->waeschepreis;
}
tankstelleDeLuxe.h:
#include "tankstelle.h"
#include <iostream>
class TankstelleDeLuxe: public Tankstelle {
double waeschepreis; // in Klassen implizit private
public:
TankstelleDeLuxe(std::string adresse="Unbekannt", double benzinpreis=0, double waeschepreis=0);
void waeschepreisEingabe(double waeschepreis);
double waeschepreisAusgabe();
};
main.cpp:
// einbinden von nützlichen Funktionen
//#include<iostream> // fuers << und >> //eigentlich schon drin
#include<vector> //fuer Vektoren
#include <stdlib.h>//fuer char umwandeln in int
#include <stdio.h> // fuer NULL
#include <unistd.h>//fuer getopt
//#include<string> //eigentlich schon da
#include<fstream> //fuer ein- und auslesen
#include<sstream> //fuer stringstream
#include<algorithm> // fuer reverse
#include "tankstelleDeLuxe.h"
using namespace std;
int main(int argc, char *argv[])
{
int c = 0;
while ((c = getopt (argc, argv, "h")) != -1){
switch (c){
case 'h': {
cout << "Dieses Programm liest n Tankstellen ein." << endl;
cout << "Sie werden aufgefordert das einzugeben." << endl;
cout << "Optionen:" << endl;
cout << "\t-h Ruft diese Hilfe auf." << endl;
return 1;
}
default:{
abort ();
}
}
}
cout << "Bitte geben sie eine Zahl an, die angibt, wieviele Tankstellen eingelesen werden.";
int n;
cin >> n;
vector<Tankstelle*> meineListe;
Tankstelle* neueTankstelle;
cout << "Soll aus einer Datei eingelesen werden?(Falls nein werden Sie aufgefordert alle Daten einzeln einzugeben) \n [J/N]";
char antwort;
cin >> antwort;
if('J'== antwort){
cout << "Geben Sie den Namen der Datei bitte an. Sie muss sich in dem Ordner befinden, indem das aktuelle Programm verwendet wird. ";
string dateiname;
string zeile;
cin >> dateiname;
ifstream datei(dateiname);
if(datei.is_open() ){
while(getline(datei, zeile) ){
stringstream zeilenbestandteile(zeile);
vector<string> meineToken;
string token;
char delimiter = ' ';
while(getline(zeilenbestandteile, token, delimiter)) {
if (token != "") {
meineToken.push_back(token);
}
}
int waschstellenanzeiger = stoi(meineToken.at(0));
double benzinpreis = stod(meineToken.at(1));
string adresse = meineToken.at(2);
if(waschstellenanzeiger == 0){
neueTankstelle = new Tankstelle(adresse, benzinpreis);
}else{
double waschpreis = stod(meineToken.at(3));
neueTankstelle = new TankstelleDeLuxe(adresse, benzinpreis, waschpreis);
}
meineListe.push_back(neueTankstelle);
}
datei.close();
}else{
cout << "Datei konnte nicht geoeffnet werden. Entweder ist sie am falschen Ort oder es liegt ein Tippfehler vor.";
return 1;
}
}else if('N'!=antwort){
cout << "Fehler bei der Eingabe. Programm beendet sich. \n";
return 1;
}else{
for(int i=0; i<n;i++){
cout << "Hat die Tankstelle eine Waschstrasse? \n [J/N]";
char eingabe;
cin >> eingabe;
double waschpreis = 0; //muss fuers compilieren aussen sein
if('J'== eingabe){
cout << "Waschpreis? (double)\n";
cin >> waschpreis;
// waschpreis = strtod(temp, NULL);
} else if ('N' != eingabe){
cout << "Eingabe hat nicht funktioniert. Das Programm wird beeindet. \n";
return 1;
}
cout << "Benzinpreis?(in double) \n ";
double benzinpreis;
cin >> benzinpreis;
// benzinpreis = strtod(temp, NULL);
cout << "Bitte die Adresse: \n ";
string adresse;
cin >> adresse;
if('J'== eingabe){
neueTankstelle = new TankstelleDeLuxe(adresse, benzinpreis, waschpreis);
}else{
neueTankstelle = new Tankstelle(adresse, benzinpreis);
}
meineListe.push_back(neueTankstelle);
}
}
delete neueTankstelle;
reverse(meineListe.begin(), meineListe.end());
cout << "Sollen die Eingaben gespeichert werden?\n[J/N] ";
char einGabe;
cin>>einGabe;
if('J'== einGabe){
fstream f;
f.open("protokoll.dat", ios::app); //erzeuge eine Datei bzw erweitere sie
for(int i=0; i<meineListe.size(); i++){
if(meineListe.at(i)->waschstrasseAusgabe()){
f << 1 << " ";
}else{
f << 0 << " ";
}
cout << "Test-Adresse fuer " << to_string(i) << ": " << meineListe.at(i)->adresseAusgabe() << endl;
f << meineListe.at(i)->benzinpreisAusgabe() << " " << meineListe.at(i)->adresseAusgabe() << " "; //lese Info in die Datei aus
if(meineListe.at(i)->waschstrasseAusgabe()){
f << to_string((static_cast<TankstelleDeLuxe*>(meineListe.at(i)))->waeschepreisAusgabe());
}
f <<endl;
}
f.close();
}
//delete meineListe;
return 0;
}
给定的test.dat。 protokoll.dat 应该是自动创建的。
【问题讨论】:
-
考虑调试程序。如果您无法使用调试器,请尝试添加调试打印。在没有真正阅读代码的情况下,一个可能的原因是,文件不是 Windows 文本文件(行尾),这会破坏解析。
-
delete neueTankstelle;表示您在meineListe中保留指向已销毁对象的指针。这是一个相当大的问题,以后会导致未定义的行为。 -
非常感谢您的 cmets!直接在引用的关键行之前的地址的调试打印给出了我希望的结果。
-
*谢谢!在 hyde :直接在引用的关键行之前的地址的调试打印给出了我希望得到的结果。 @molbdnilo:将删除行移至返回前的最新行。它解决了问题!下周我将与我的朋友讨论删除问题,但很尴尬我没有认识到这种未定义行为的案例。我不知道,为什么 g++ 在 Linux 和 Windows w.r.t 上的行为不同。未定义的行为不同(关于 g++ 源代码),但现在,我理解了问题 w。河。吨。标准。如果你写你的评论作为答案,我会接受。
-
如果您有时间,请随意自己写答案(并将其标记为已接受)。使用电话,我现在不想写正确的答案:-)