在多映射示例中使用指向 class 对象的指针作为键
Using pointer to class objects as key in a multimap example
我读了很多书,但我似乎无法理解这应该如何工作。本程序原本使用成员multimap<CFile, Filetype>
,但我需要重新制作成multimap<CFile*, Filetype>
。根据我对指针的小理解,我需要制作一个多图,它以指向 CFile
对象的指针作为键,但我未能实现这一点。我实际上在 CDirectory
构造函数中将指针放在 multimap 中,但是当我尝试打印 CDirectory 的对象时程序崩溃,所以(许多错误中的一个)是我为 [=15 重载 ostream operator<<
=] class。我真的不知道从哪里开始,我得到了基本的指针逻辑,但我似乎无法实现它。
class CFile {
string m_strFile;
unsigned int m_size;
public:
/*constructors, get() and set() functions are
implemented but I've deleted them for the example */
bool operator< (const CFile& obj) const {
return (m_strFile < obj.m_strFile);
}
bool operator== (const CFile& obj) const {
return (m_size == obj.m_size);
}
friend ostream& operator<< (ostream& ost, const CFile& obj) {
return ost << "name: " << obj.m_strFile << ", size: " << obj.m_size;
}
friend istream& operator>> (istream& ist, CFile& obj) {
return ist >> obj.m_strFile >> obj.m_size;
}
};
class CDirectory {
string m_strDirectory;
enum class Filetype {
Archive, Hidden, ReadOnly, System, FileNotSupported
};
multimap <CFile*, Filetype> m_DirectoryMap;
public:
/* overloading operator<< for class CDirectory and uses the friend function filetypeToString to convert the enum value to string */
friend std::ostream& operator<<(std::ostream &os, const CDirectory &dir) {
os << dir.m_strDirectory << "\n";
auto p = m_DirectoryMap.begin();
while ( p != m_DirectoryMap.end()) {
os << p->first->getFileName() << '\t' << p->first->getFileSize() << '\t' << CDirectory::filetypeToString(p->second) << '\n';
++p;
}
return os;
}
/* comparator function, used to find the min and max files by size
- takes 2 pairs of the multimap and compares their CFile objects filesize */
static bool Greater(const pair<const CFile, Filetype>& a,
const pair<const CFile, Filetype>& b) {
return (a.first.getFileSize() < b.first.getFileSize());
}
/* explicit constructor - reads data from a file and inserts pairs
of types pair <CFile, enum Filetype> in a multimap */
CDirectory (const string& n) {
fp.open (n, ios::in);
if (!fp) {
throw std::runtime_error("Could not open file");
}
string dirName, fileName, fType;
int fileSize;
Filetype filetype;
fp >> dirName;
m_strDirectory = dirName;
while (fp >> fileName >> fileSize >> fType) {
CFile obj (fileName, fileSize);
CFile* ptr = &obj;
if (fType == "Archive")
filetype = Filetype::Archive;
else if (fType == "Hidden")
filetype = Filetype::Hidden;
else if (fType == "ReadOnly")
filetype = Filetype::ReadOnly;
else if (fType == "System")
filetype = Filetype::System;
else
filetype = Filetype::FileNotSupported;
m_DirectoryMap.insert(pair<CFile*, Filetype>(ptr, Filetype(filetype)));
}
}
string getDirectory () const { return m_strDirectory; }
void printMap () {
auto p = m_DirectoryMap.begin();
cout << m_strDirectory << endl;
while ( p != m_DirectoryMap.end()) {
cout << endl << p->first->getFileName() << '\t' << p->first->getFileSize() << '\t' << filetypeToString(p->second) << endl;
++p;
}
}
int countDuplicates( const string& strToCount ) const {
CFile obj (strToCount, 0);
CFile* ptr = &obj;
int numberOfDuplicates = m_DirectoryMap.count(ptr);
if (numberOfDuplicates > 1)
return numberOfDuplicates;
else if (numberOfDuplicates == 1)
return 1;
else
return 0;
}
void removeDuplicates( const string& strToRemove ) {
CFile obj (strToRemove, 0);
CFile* ptr = &obj;
pair <multimap<CFile*,Filetype>::iterator, multimap<CFile*,Filetype>::iterator> range;
range = m_DirectoryMap.equal_range(ptr);
auto it = range.first;
++it;
while (it != range.second)
it = m_DirectoryMap.erase(it);
}
/* CFile findMaxSize() const {
multimap<CFile*, Filetype>::const_iterator result;
result = std::max_element(m_DirectoryMap.begin(), m_DirectoryMap.end(), Greater);
CFile obj(result.first->getFileName(), result.first->getFileSize());
return obj;
}
CFile findMinSize() const {
multimap<CFile*, Filetype>::const_iterator result;
result = std::min_element(m_DirectoryMap.begin(), m_DirectoryMap.end(), Greater);
CFile obj(result.first->getFileName(), result.first->getFileSize());
return obj;
} */
static std::string filetypeToString(Filetype type) {
switch (type) {
case Filetype::Archive:
return "archive";
break;
case Filetype::Hidden:
return "hidden";
break;
case Filetype::ReadOnly:
return "read-only";
break;
case Filetype::System:
return "system";
break;
case Filetype::FileNotSupported:
return "not-supported";
break;
}
}
};
int main () {
/* - Catching if the file exists, prompt the user untill a correct name is given
- Making an object of type CDirectory, reading data from a file and inserting
pairs of <CFile, Filetype> in a multimap.
- Counting the number of duplicated filenames and removing them (leaving only 1).
- Finding the min and max files by size and printing their objects. */
string filename = "";
int numberOfDuplicates = 0;
cout << "Please enter input file name: \n";
string iname = "";
bool done = false;
CDirectory obj();
while (!done && cin >> iname) {
ifstream ist{iname};
try {
CDirectory obj(iname);
done = true;
cout << "The original multimap (ordered by filename) contains the following data: \n\n";
system("pause");
cout << obj;
cout << "\n\nCheck if the file has any duplicates. Enter a filename:\n\n";
do {
cin >> filename;
numberOfDuplicates = obj.countDuplicates(filename);
if ( numberOfDuplicates > 1) {
cout << "The file has " << numberOfDuplicates << " duplicates.";
cout << "Removing duplicates of " << filename << ". \n\n";
}
else if (numberOfDuplicates == 1)
cout << "The file " << filename << " does not have any duplicates.\n\n";
else if (numberOfDuplicates == 0)
cout << "The file " << filename << " is not in the multimap. Please enter a new filename:\n\n";
} while (!(numberOfDuplicates > 0));
system("pause");
obj.removeDuplicates(filename);
cout << "The updated multimap (ordered by filename) contains the following data: \n\n";
cout << obj;
} catch (std::exception &ex) {
std::cout << ex.what() << "!\n" << "Please try again.\n";
}
}
getch();
return 0;
}
在
while (fp >> fileName >> fileSize >> fType) {
CFile obj (fileName, fileSize);
CFile* ptr = &obj;
if (fType == "Archive")
filetype = Filetype::Archive;
else if (fType == "Hidden")
filetype = Filetype::Hidden;
else if (fType == "ReadOnly")
filetype = Filetype::ReadOnly;
else if (fType == "System")
filetype = Filetype::System;
else
filetype = Filetype::FileNotSupported;
m_DirectoryMap.insert(pair<CFile*, Filetype>(ptr, Filetype(filetype)));
}
你用
创建了一个循环局部变量obj
CFile obj (fileName, fileSize);
然后用
存储指向该对象的指针
CFile* ptr = &obj;
//...
m_DirectoryMap.insert(pair<CFile*, Filetype>(ptr, Filetype(filetype)));
然后你重新开始循环。不幸的是,当您到达循环的末尾时,所有循环对象都被销毁,然后在循环从头开始时重新创建它们。这意味着地图现在有一个指向被销毁对象的指针。该指针不再有效,使用它是未定义的行为。
一个快速的解决方法是创建一个指针而不是像
这样的自动对象
CFile* obj = new CFile(fileName, fileSize);
然后将该指针存储在地图中。这确实意味着您需要在完成映射后清理分配的内存。您需要遍历地图并在每个键上调用 delete
。
然后我们有另一个问题,当使用
multimap <CFile*, Filetype> m_DirectoryMap;
地图将按指针持有的地址而不是指向 CFile
的地址对键进行排序。要按实际 CFile
对地图进行排序,您需要编写一个自定义比较器,它将使用两个 CFile*
和 returns 来比较指向 CFile
的指针。
我读了很多书,但我似乎无法理解这应该如何工作。本程序原本使用成员multimap<CFile, Filetype>
,但我需要重新制作成multimap<CFile*, Filetype>
。根据我对指针的小理解,我需要制作一个多图,它以指向 CFile
对象的指针作为键,但我未能实现这一点。我实际上在 CDirectory
构造函数中将指针放在 multimap 中,但是当我尝试打印 CDirectory 的对象时程序崩溃,所以(许多错误中的一个)是我为 [=15 重载 ostream operator<<
=] class。我真的不知道从哪里开始,我得到了基本的指针逻辑,但我似乎无法实现它。
class CFile {
string m_strFile;
unsigned int m_size;
public:
/*constructors, get() and set() functions are
implemented but I've deleted them for the example */
bool operator< (const CFile& obj) const {
return (m_strFile < obj.m_strFile);
}
bool operator== (const CFile& obj) const {
return (m_size == obj.m_size);
}
friend ostream& operator<< (ostream& ost, const CFile& obj) {
return ost << "name: " << obj.m_strFile << ", size: " << obj.m_size;
}
friend istream& operator>> (istream& ist, CFile& obj) {
return ist >> obj.m_strFile >> obj.m_size;
}
};
class CDirectory {
string m_strDirectory;
enum class Filetype {
Archive, Hidden, ReadOnly, System, FileNotSupported
};
multimap <CFile*, Filetype> m_DirectoryMap;
public:
/* overloading operator<< for class CDirectory and uses the friend function filetypeToString to convert the enum value to string */
friend std::ostream& operator<<(std::ostream &os, const CDirectory &dir) {
os << dir.m_strDirectory << "\n";
auto p = m_DirectoryMap.begin();
while ( p != m_DirectoryMap.end()) {
os << p->first->getFileName() << '\t' << p->first->getFileSize() << '\t' << CDirectory::filetypeToString(p->second) << '\n';
++p;
}
return os;
}
/* comparator function, used to find the min and max files by size
- takes 2 pairs of the multimap and compares their CFile objects filesize */
static bool Greater(const pair<const CFile, Filetype>& a,
const pair<const CFile, Filetype>& b) {
return (a.first.getFileSize() < b.first.getFileSize());
}
/* explicit constructor - reads data from a file and inserts pairs
of types pair <CFile, enum Filetype> in a multimap */
CDirectory (const string& n) {
fp.open (n, ios::in);
if (!fp) {
throw std::runtime_error("Could not open file");
}
string dirName, fileName, fType;
int fileSize;
Filetype filetype;
fp >> dirName;
m_strDirectory = dirName;
while (fp >> fileName >> fileSize >> fType) {
CFile obj (fileName, fileSize);
CFile* ptr = &obj;
if (fType == "Archive")
filetype = Filetype::Archive;
else if (fType == "Hidden")
filetype = Filetype::Hidden;
else if (fType == "ReadOnly")
filetype = Filetype::ReadOnly;
else if (fType == "System")
filetype = Filetype::System;
else
filetype = Filetype::FileNotSupported;
m_DirectoryMap.insert(pair<CFile*, Filetype>(ptr, Filetype(filetype)));
}
}
string getDirectory () const { return m_strDirectory; }
void printMap () {
auto p = m_DirectoryMap.begin();
cout << m_strDirectory << endl;
while ( p != m_DirectoryMap.end()) {
cout << endl << p->first->getFileName() << '\t' << p->first->getFileSize() << '\t' << filetypeToString(p->second) << endl;
++p;
}
}
int countDuplicates( const string& strToCount ) const {
CFile obj (strToCount, 0);
CFile* ptr = &obj;
int numberOfDuplicates = m_DirectoryMap.count(ptr);
if (numberOfDuplicates > 1)
return numberOfDuplicates;
else if (numberOfDuplicates == 1)
return 1;
else
return 0;
}
void removeDuplicates( const string& strToRemove ) {
CFile obj (strToRemove, 0);
CFile* ptr = &obj;
pair <multimap<CFile*,Filetype>::iterator, multimap<CFile*,Filetype>::iterator> range;
range = m_DirectoryMap.equal_range(ptr);
auto it = range.first;
++it;
while (it != range.second)
it = m_DirectoryMap.erase(it);
}
/* CFile findMaxSize() const {
multimap<CFile*, Filetype>::const_iterator result;
result = std::max_element(m_DirectoryMap.begin(), m_DirectoryMap.end(), Greater);
CFile obj(result.first->getFileName(), result.first->getFileSize());
return obj;
}
CFile findMinSize() const {
multimap<CFile*, Filetype>::const_iterator result;
result = std::min_element(m_DirectoryMap.begin(), m_DirectoryMap.end(), Greater);
CFile obj(result.first->getFileName(), result.first->getFileSize());
return obj;
} */
static std::string filetypeToString(Filetype type) {
switch (type) {
case Filetype::Archive:
return "archive";
break;
case Filetype::Hidden:
return "hidden";
break;
case Filetype::ReadOnly:
return "read-only";
break;
case Filetype::System:
return "system";
break;
case Filetype::FileNotSupported:
return "not-supported";
break;
}
}
};
int main () {
/* - Catching if the file exists, prompt the user untill a correct name is given
- Making an object of type CDirectory, reading data from a file and inserting
pairs of <CFile, Filetype> in a multimap.
- Counting the number of duplicated filenames and removing them (leaving only 1).
- Finding the min and max files by size and printing their objects. */
string filename = "";
int numberOfDuplicates = 0;
cout << "Please enter input file name: \n";
string iname = "";
bool done = false;
CDirectory obj();
while (!done && cin >> iname) {
ifstream ist{iname};
try {
CDirectory obj(iname);
done = true;
cout << "The original multimap (ordered by filename) contains the following data: \n\n";
system("pause");
cout << obj;
cout << "\n\nCheck if the file has any duplicates. Enter a filename:\n\n";
do {
cin >> filename;
numberOfDuplicates = obj.countDuplicates(filename);
if ( numberOfDuplicates > 1) {
cout << "The file has " << numberOfDuplicates << " duplicates.";
cout << "Removing duplicates of " << filename << ". \n\n";
}
else if (numberOfDuplicates == 1)
cout << "The file " << filename << " does not have any duplicates.\n\n";
else if (numberOfDuplicates == 0)
cout << "The file " << filename << " is not in the multimap. Please enter a new filename:\n\n";
} while (!(numberOfDuplicates > 0));
system("pause");
obj.removeDuplicates(filename);
cout << "The updated multimap (ordered by filename) contains the following data: \n\n";
cout << obj;
} catch (std::exception &ex) {
std::cout << ex.what() << "!\n" << "Please try again.\n";
}
}
getch();
return 0;
}
在
while (fp >> fileName >> fileSize >> fType) {
CFile obj (fileName, fileSize);
CFile* ptr = &obj;
if (fType == "Archive")
filetype = Filetype::Archive;
else if (fType == "Hidden")
filetype = Filetype::Hidden;
else if (fType == "ReadOnly")
filetype = Filetype::ReadOnly;
else if (fType == "System")
filetype = Filetype::System;
else
filetype = Filetype::FileNotSupported;
m_DirectoryMap.insert(pair<CFile*, Filetype>(ptr, Filetype(filetype)));
}
你用
创建了一个循环局部变量obj
CFile obj (fileName, fileSize);
然后用
存储指向该对象的指针CFile* ptr = &obj;
//...
m_DirectoryMap.insert(pair<CFile*, Filetype>(ptr, Filetype(filetype)));
然后你重新开始循环。不幸的是,当您到达循环的末尾时,所有循环对象都被销毁,然后在循环从头开始时重新创建它们。这意味着地图现在有一个指向被销毁对象的指针。该指针不再有效,使用它是未定义的行为。
一个快速的解决方法是创建一个指针而不是像
这样的自动对象CFile* obj = new CFile(fileName, fileSize);
然后将该指针存储在地图中。这确实意味着您需要在完成映射后清理分配的内存。您需要遍历地图并在每个键上调用 delete
。
然后我们有另一个问题,当使用
multimap <CFile*, Filetype> m_DirectoryMap;
地图将按指针持有的地址而不是指向 CFile
的地址对键进行排序。要按实际 CFile
对地图进行排序,您需要编写一个自定义比较器,它将使用两个 CFile*
和 returns 来比较指向 CFile
的指针。