[英]C++ : Abstract classes
我仍在學習抽象類是如何工作的,我想知道我是否走在正確的軌道上。
這是我的簡化程序:
class CPerson
{
public:
CPerson() = default;
virtual int getMat() = 0;
};
class CStudent : public CPerson
{
public:
CStudent() = default;
CStudent(int MatriculationNr) { this->MatriculationNr = MatriculationNr;}
int getMat() { return MatriculationNr;}
private:
int MatriculationNr;
};
class CTeacher : public CPerson
{
public:
CTeacher() = default;
int getMat(){ return 0;}
private:
std::string name;
};
int main()
{
std::vector<CPerson*> test;
test.push_back(new CStudent(9898));
CTeacher *a = new CTeacher();
return 0;
}
在 class CTeacher
中,我沒有像CStudent
( MatriculationNr
) 一樣的私有變量,所以我返回 0。程序運行正常。 但是我在這里所做的是否正確?
另一個與抽象類有關的問題:假設我們使用virtual int& getMat() = 0;
(有參考),我們應該在CTeacher
class中返回什么? 0 在這種情況下不起作用,對吧?
我們應該用 0 初始化一個變量,以便我們可以在這個 function 中返回它,還是有更好的實現?
下面的代碼示例應該以相當現代的 C++ 方式回答您的問題。
#include <iostream>
#include <string>
#include <vector>
#include <memory>
// I have added a `std::string name` to the CPerson class and
// a `std::string subject` to the CTeachers class
// so both derived classes, CStudent and CTeacher have a name
// of the person involved and each derived class has
// something only it needs, MatrikulationNr for CStudent and
// Subject of teaching for CTeacher in order to deliver a more
// complete and more clearifying answer.
class CPerson {
int dummyMatriculationNr{ 0 };
std::string dummySubject{ "noTeacher" };
protected:
std::string name;
public:
std::string getName() { return name; }
virtual int& getMat() { return dummyMatriculationNr; }
virtual std::string getSubject() { return dummySubject; }
};
class CStudent : public CPerson {
int MatriculationNr{ 0 };
public:
CStudent() = delete; // we dont want anyone using this constructor
explicit CStudent(std::string name, int MatriculationNr) :
MatriculationNr{ MatriculationNr } {
this->name = name;
}
int& getMat() { return MatriculationNr; }
};
class CTeacher : public CPerson {
std::string subject{ "" }; // Subject of teaching
public:
CTeacher() = delete;
explicit CTeacher(std::string name, std::string subject) :
subject{ subject } {
this->name = name;
}
std::string getSubject() { return subject; }
};
int main() {
std::vector<std::unique_ptr<CPerson>> vp;
vp.push_back(std::make_unique<CStudent>("aStudentsName", 8989 ));// or emplace_back
vp.push_back(std::make_unique<CTeacher>("aTeachersName", "mathematics"));
for (auto& e : vp)
std::cout << "Name: " << e->getName() << " MatrNo: " << e->getMat()
<< " TeachingSubject: " << e->getSubject() << std::endl;
}
我希望以上示例能回答您的問題。 但是,使用關鍵字virtual
會在運行時創建一個虛擬 function 表,通常稱為 vtable,這會消耗性能並且不再被認為是高性能計算。
如果您只需要一個getMat()
function 在所有派生類中都可用,這也令人困惑, CStudents
派生 class。 雖然在任何其他 class 中沒有意義,但這個 function 仍然在那里返回一些虛擬值。 這可能很煩人。 CTeacher 中的getSubject()
function 也是如此。 參見 output:
Name: aStudentsName MatrNo: 8989 TeachingSubject: noTeacher
Name: aTeachersName MatrNo: 0 TeachingSubject: mathematics
考慮在沒有任何關鍵字virtual
並且僅在CStudents
中而不在基礎 class 中使用getMat()
和int MatriculationNr
的情況下解決您的問題。 我知道使用 virtual 很誘人,但要盡可能避免使用它。 例如,MFC,Microsoft Foundation Classes,可能是最大的 class inheritance 項目曾經編寫過,根本沒有使用虛擬:考慮以下代碼作為示例:
#include <iostream>
#include <string>
#include <vector>
#include <variant>
// a code version without virtual
class CPerson {
protected:
std::string name;
public:
std::string getName() { return name; }
};
class CStudent : public CPerson {
int MatriculationNr{ 0 };
public:
CStudent() = delete; // we dont want anyone using this constructor
explicit CStudent(std::string name, int MatriculationNr) : MatriculationNr{ MatriculationNr } {
this->name = name;
}
int& getMat() { return MatriculationNr; }
};
class CTeacher : public CPerson {
std::string subject{ "" }; // Subject of teaching
public:
CTeacher() = delete;
explicit CTeacher(std::string name, std::string subject) : subject{ subject } {
this->name = name;
}
std::string getSubject() { return subject; }
};
int main() {
std::vector<CStudent> vs; // auto-deleted through RAII
std::vector<CTeacher> vt; // and good for serialisation and or database communication
vs.push_back(CStudent{ "aStudentsName", 9898 });
vt.push_back(CTeacher{ "aTeachersName", "mathematics" });
for (auto s : vs)
std::cout << s.getName() << " " << s.getMat() << std::endl;
for (auto t : vt)
std::cout << t.getName() << " " << t.getSubject() << std::endl << std::endl;
// and here we are done already,
// not listing the two different types in one vector
// but just using a vector for each derived class
//
// but lets try put them now into one vector
// using a more modern way through std::variant
// which keps all data in the vector and not only the
// CPerson part.
std::vector<std::variant<CStudent, CTeacher>> people;
// we could, for example, copy from above vectors
for (auto e : vs)
people.push_back(e);
for (auto e : vt)
people.push_back(e);
// we could insert new ones
people.push_back(CStudent { "aStudentsName1", 9899 });
people.push_back(CTeacher { "aTeachersName1", "physics" });
// and take that vector apart again
std::cout << std::endl << "Output from vector of variant:" << std::endl;
for (auto& e : people)
if (std::holds_alternative<CStudent>(e)) {
CStudent& s = std::get<CStudent>(e);
std::cout << s.getName() << " " << s.getMat() << std::endl;
}
else if (std::holds_alternative<CTeacher>(e)) {
CTeacher& s = std::get<CTeacher>(e);
std::cout << s.getName() << " " << s.getSubject() << std::endl;
}
}
有很多方法可以實現避免虛擬的目標,我希望你喜歡上面的方法。
上面代碼中的一切似乎都很好。 只需確保在使用它們后刪除這兩個對象( CStudent
和CTeacher
)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.