01/10/2018, 11:44

Hỏi cách sử dụng Lớp cơ sở trừu tượng trong C++

Tôi là newbie đang bắt đầu tự học lập trình qua đọc sách là chủ yếu. Hiện tại đang gặp vấn đề ở phần ví dụ của Lớp cơ sở trừu tượng, nhờ các Tiền Bối chỉ bảo dùm.

    #include <iostream> 
    #include <math.h> 
     
    using namespace std; 
     
    class Point 
    { 
    protected: 
      float x, y; 
    public: 
      float& getX(void); 
      float& getY(void); 
      virtual float KhoangCach(void*)=0; 
    }; 
     
    float& Point::getX(void) 
    { 
      return x; 
    } 
     
    float& Point::getY(void) 
    { 
      return y; 
    }     
    class Point2D:public Point 
    { 
    public: 
      float KhoangCach(void*); 
    }; 
     
    class Point3D:public Point 
    {
    float z; 
    public: 
      float& getZ(void); 
      float KhoangCach(void*); 
    }; 
     
    float Point2D::KhoangCach(void* p) 
    { 
      Point2D* q = (Point2D*)(p); 
      return sqrt(pow(q->x-x,2)+pow(q->y-y,2));  
    } 
     
    float& Point3D::getZ(void) 
    { 
      return z; 
    } 
     
    float Point3D::KhoangCach(void* p) 
    { 
      Point3D* q = (Point3D*)(p); 
      return sqrt(pow(q->x-x,2)+pow(q->y-y,2)+pow(q->z-z,2));  
    } 
     
    int main() 
    { 
      Point2D v2; 
      Point2D u2; 
      cout<<".::-----Nhap toa do cho hai Point2D u va v-----::."<<endl; 
      cout<<"+ Toa do u:"<<endl; 
      cin>>v2.getX(); 
      cin>>v2.getY(); 
      cout<<"+ Toa do v:"<<endl; 
      cin>>u2.getX(); 
      cin>>u2.getY(); 
      Point* u = &v2; 
      Point* v = &u2; 
      cout<<"D(u,v) = "<<u->KhoangCach(v); 
      Point3D v3; 
      Point3D u3; 
      cout<<endl<<".::-----Nhap toa do cho hai Point3D u va v-----::."<<endl; 
      cout<<"+ Toa do u:"<<endl; 
      cin>>v3.getX(); 
      cin>>v3.getY(); 
      cin>>v3.getZ(); 
      cout<<"+ Toa do v:"<<endl; 
      cin>>u3.getX(); 
      cin>>u3.getY(); 
      cin>>u3.getZ(); 
      Point* p = &v3; 
      Point* q = &u3; 
      cout<<"D(u3,v3) = "<<p->KhoangCach(q); 
      cout<<endl; 
      return 0; 
    } 

Phần tính khoảng cách của lớp Point2D và Point3D khó hiểu quá. Tại sao phải đặt Point2D *q=(Point2D*)(p) để làm gì? q->x: là x của tham số nào? Và -x: là trừ x của tham số nào? Rối quá.
Mong được gỡ rối dùm.
Cảm ơn.

rogp10 viết 13:44 ngày 01/10/2018

Điều khó hiểu hơn là vì sao không dùng reference đặt gạch

Thi Đỗ Gia Thành viết 13:52 ngày 01/10/2018

Chắc tại tác giả muốn dùng ví dụ này để áp dụng Lớp cơ sở trừu tượng.
Còn phần Reference thì trong sách tôi đang đọc vẫn chưa thấy nói tới.

Dark.Hades viết 13:57 ngày 01/10/2018

Nhận xét:
Lớp Point3D còn chưa có constructor.

Thi Đỗ Gia Thành viết 13:48 ngày 01/10/2018

“constructor” thì tôi không rỏ, có phải ý của Dark.Hades là hàm tạo không?
Ở đây không dùng hàm tạo mà chương trình sẽ yêu cầu ta nhập vào các đặc trưng của đối tượng.

rogp10 viết 13:47 ngày 01/10/2018

Thiếu const ở tham số.

Thực ra dòng ép kiểu đó không cần thiết. Viết hẳn lên chỗ tham số luôn.

Thi Đỗ Gia Thành viết 13:46 ngày 01/10/2018

rogp10 muốn nói tham số của hàm nào thiếu cosnt? chương trình vẫn chạy đúng mà.

Dark.Hades viết 13:59 ngày 01/10/2018

Vì cái chương trình của bạn dùng quá nhiều tham chiếu (void), để làm gì thì mình không rõ, có lẽ tránh lỗi khi không may viết nhầm tham chiếu chăng??
Viết như vậy đến lúc tham chiếu nhầm giá trị mà cứ ép kiểu thì …
Nên xoá hết void, sử dụng nạp chồng hàm để chương trình rõ ràng

Các hàm get sẽ theo dạng:
getSomeThing() const
Hàm set sẽ theo dạng:
setSomeThing(const &ref)

Còn phần constructor mình nói phía trên là Point 3d sẽ có 3 chiều, tuy nhiên chương trình của bạn thì chỉ kế thừa lại Point2D mà không xây lại hàm nhập chiều Z

Thi Đỗ Gia Thành viết 13:45 ngày 01/10/2018

Xin cảm ơn những chia sẽ của các tiền bối.
Nhưng hiện tại tôi chỉ mới học được một vài thứ thôi nên những gợi ý thay đổi chương trình ở trên thì tôi lại càng không hiểu.
Có lẽ chương trình này chưa phải là tốt nhất để tính khoảng cách 2 điểm, nhưng nó dùng để minh họa cái lý thuyết mà tôi đang học là “Lớp cơ sở trừu tượng”.
Do không hiểu được ví dụ này nên tôi sẽ không thể sử dụng được nó (Lớp cơ sở trừu tượng) trong những trường hợp thật sự cần thiết.
Chương trình này đã chạy đúng rồi, nhờ tiền bối nào hiểu được thì giải thích dùm những thắt mắc của tôi ở trên.
Xin cảm ơn.

rogp10 viết 13:46 ngày 01/10/2018

Nói sơ:

Phương thức virtual có tính chất khi gọi qua một ref của superclass thì phương thức của subclass luôn được gọi, chứ không phải superclass. Đây là nền tảng cho đa hình (gom nhiều đối tượng của subclass…)

Pure virtual (gán bằng 0 như trên) làm cho chính superclass không thể instantiate được, hay nó là trừu tượng do không thể có đối tượng của nó, kéo theo các subclass buộc phải override nó.

Khi một superclass có toàn pure virtual (gồm pure destructor) thì có thể xem là interface và không gặp vấn đề với multi inheritance.

Thi Đỗ Gia Thành viết 13:52 ngày 01/10/2018

Cảm ơn rogp10!
Góp ý trên giúp tôi hiểu được đơn giản hơn về các khái niệm. Lúc tự đọc thấy hơi mơ hồ.

Thi Đỗ Gia Thành viết 13:55 ngày 01/10/2018

Tôi vẫn chưa hiểu được cú pháp chi tiết của ví dụ trên. Nhờ các cao thủ chỉ bảo dùm.

rogp10 viết 13:45 ngày 01/10/2018

virtual <prototype của phương thức yêu cầu override> = 0;

Vậy ta nên viết lại prototype như sau:
virtual KhoangCach(const Point &p) = 0; Chú ý p này có kiểu là Point&.

Để void* rất nguy hiểm vì cái gì cũng truyền vào được. Để cho chắc, bạn nên tìm hiểu về dynamic_cast.

p/s: thực ra destructor nên cho virtual hết, vì khi đã cần viết destructor tức là phải free cái gì đó (rule of 3) nên destructor PHẢI được gọi đúng.

VD: một class Logging giữ một file handle để ghi nhật trình (log). Resource được giữ là quyền exclusive writing lên file đó, nên áp dụng rule of 3. Giờ class LoggingMoar th.k Logging mở thêm cái file nữa thì sao

Bài liên quan
0