簡體   English   中英

C++:抽象類

[英]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;
        }

}

有很多方法可以實現避免虛擬的目標,我希望你喜歡上面的方法。

上面代碼中的一切似乎都很好。 只需確保在使用它們后刪除這兩個對象( CStudentCTeacher )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM