virtual funktsiyani ko'rsatuvchi chalkashlik

Tenglangan sinflarda asosiy klassning bekor qilingan virtual funktsiyalariga kirish uchun asosiy sinfi uchun markerni ishlatishimiz mumkinligini bilamiz.

Quyidagilar bunga misoldir.

#include 

class shape {
public:
    virtual void draw() {
    std::cout << "calling shape::draw()\n";
    }
};

class square : public shape {
public:
    virtual void draw() {
        std::cout << "calling square::draw()\n";
    }
    int area() {
        return width*width;
    }
    square(int w) {
        width = w;
    }
    square() {
        width = 0;
    }

protected:
    int width;
};

class rect : public square {
public:
    virtual void draw() {
        std::cout << "calling rect::draw()\n";
    }

    int area() {
        return width*height;
    }
    rect(int h, int w) {
        height = h;
        width = w;
    }
protected:
    int height;
};

int main() {
    /*
    shape* pshape[3] = {
        new shape,
        new square(2),
        new rect(2, 3)
        };

    for (int i = 0; i<3; i++){
        pshape[i]->draw();
    }
    */
    square* psquare = new rect(2, 3);
    psquare->draw();
    system("pause");
    return 0;
}

pshape [i] ko'rsatgichlari draw() virtual funktsiyasiga osongina kira oladi. Endi bosh murabbiy uchun. "Kvadrat" klassi "rekt" klassining asosiy klassidir. Shuning uchun kvadrat * belgisi mavjud bo'lsa, "rect" ( square = psquare = new rect (2, 3) ) draw() funktsiyasiga kirishingiz mumkin; chiqdi:

calling rect::draw()
Press any key to continue . . .

Endi agar "virtual" kalit so'zini kvadratdan (draw() belgilanganidan olib tashlasam, u holda kod hali tayyorlanmaydi va chiqish bir xil bo'ladi:

calling rect::draw()
Press any key to continue . . .

Finally if I remove 'virtual' from the base function, the output of psquare->draw() is:

calling square::draw()
Press any key to continue . . .

Bu meni mudhish narsadir. Bu erda nima sodir bo'ladi?

  1. Since square is parent of rect class, square should have its draw() function to be virtual in order to let rect override it. But it still is compiling and giving the same output as when it is virtualized.
  2. Since shape is the base class of all, removing the virtual keyword of draw() in shape should result in error. But that's not happening, it is compiling and giving another output calling square::draw() when psquare->draw() is called.

Men juda ko'p narsalarda xatoga yo'l qo'yishim mumkin. Iltimos, nima noto'g'ri ekanligini aniqlang va aynan nima sodir bo'lganini ayting.

0
Sizning sinfingiz hiyerarşisi atrofida noto'g'ri yo'l. To'rtburchak uzunligi va kengligi bir xil bo'lgan kvadratdir. Lekin sizning sinf hiyerarşisi, to'rtburchakning kvadrat, deb aytilmagan.
qo'shib qo'ydi muallif David Schwartz, manba
"square" klassi "rect" sinfining asosiy klassi bu juda keng tarqalgan dizayn xatoligami, biroq bu xatolik.
qo'shib qo'ydi muallif n.m., manba
"square" klassi "rect" sinfining asosiy klassi bu juda keng tarqalgan dizayn xatoligami, biroq bu xatolik.
qo'shib qo'ydi muallif n.m., manba
'O'chirish'. "Yozib bo'lmaydi".
qo'shib qo'ydi muallif EJP, manba
'O'chirish'. "Yozib bo'lmaydi".
qo'shib qo'ydi muallif EJP, manba
'O'chirish'. "Yozib bo'lmaydi".
qo'shib qo'ydi muallif EJP, manba
Yaxshisi siz aralashgan xatti-harakatlaringizni aniq ko'rsatadigan eng kichik, to'liq va tasdiqlanadigan misol .
qo'shib qo'ydi muallif juanchopanza, manba
Yaxshisi siz aralashgan xatti-harakatlaringizni aniq ko'rsatadigan eng kichik, to'liq va tasdiqlanadigan misol .
qo'shib qo'ydi muallif juanchopanza, manba
@DavidSchwartz Aslida na na square-rectangle-problem ga qarang.
qo'shib qo'ydi muallif nwp, manba

9 javoblar

Agar funktsiya asosiy sinfda virtual deb e'lon qilingan bo'lsa, xuddi o'sha erda virtual kalit so'zini qo'yganidek, barcha türemiş sinflarga avtomatik ravishda virtual bo'ladi. Tarkibdagi funktsiyalar uchun C ++ "virtual" kalit so'zini ko'ring. sinflar. Zarur kerakmi? .

Agar funktsiya virtual bo'lmasa, qaysi versiya chaqirilsa, uni chaqiradigan ko'rsatgichning turiga bog'liq bo'ladi. Tenglangan sinfning har bir namunasi ota-ona sinflarining har birining namunasidir, chunki ota-ona sinfida a'zo funktsiyasini chaqirish juda yaxshi. Xatolik yo'q.

3
qo'shib qo'ydi

Agar funktsiya asosiy sinfda virtual deb e'lon qilingan bo'lsa, xuddi o'sha erda virtual kalit so'zini qo'yganidek, barcha türemiş sinflarga avtomatik ravishda virtual bo'ladi. Tarkibdagi funktsiyalar uchun C ++ "virtual" kalit so'zini ko'ring. sinflar. Zarur kerakmi? .

Agar funktsiya virtual bo'lmasa, qaysi versiya chaqirilsa, uni chaqiradigan ko'rsatgichning turiga bog'liq bo'ladi. Tenglangan sinfning har bir namunasi ota-ona sinflarining har birining namunasidir, chunki ota-ona sinfida a'zo funktsiyasini chaqirish juda yaxshi. Xatolik yo'q.

3
qo'shib qo'ydi

Agar funktsiya asosiy sinfda virtual deb e'lon qilingan bo'lsa, xuddi o'sha erda virtual kalit so'zini qo'yganidek, barcha türemiş sinflarga avtomatik ravishda virtual bo'ladi. Tarkibdagi funktsiyalar uchun C ++ "virtual" kalit so'zini ko'ring. sinflar. Zarur kerakmi? .

Agar funktsiya virtual bo'lmasa, qaysi versiya chaqirilsa, uni chaqiradigan ko'rsatgichning turiga bog'liq bo'ladi. Tenglangan sinfning har bir namunasi ota-ona sinflarining har birining namunasidir, chunki ota-ona sinfida a'zo funktsiyasini chaqirish juda yaxshi. Xatolik yo'q.

3
qo'shib qo'ydi

Birinchidan, derleyici nimani ko'rishi haqida o'ylab ko'ring. Belgilangan ko'rsatgich uchun ierarxiyani yuqoriga qarab kuzatib boradi va agar u xuddi shu usulni virtual deb topsa, u holda dinamik ish vaqti chaqiruvi chiqariladi. Agar u virtual ni ko'rmasa, u holda chiqadigan chaqiruv funktsiyaning "eng past" ta'rifiga mos keladigan turga (yoki birinchi turdagi toplardan yuqoriga qarab topilgan) to'g'ri keladi. Shunday qilib (agar sizda Square * p = new Rectangle() bo'lsa):

Shape { virtual draw() }
Square : Shape { virtual draw() }
Rectangle : Square { virtual draw() }

har bir narsa aniq, har doim virtual.

Agar shunday bo'lsa:

Shape { virtual draw() }
Square : Shape { draw() }
Rectangle : Square { virtual draw() }

shunda derleyici Shape-ning o'yini virtual ekanligini ko'rsa, unda qo'ng'iroq dinamik bo'ladi va Rectangle :: draw deb ataladi.

Agar shunday bo'lsa:

Shape { draw() }
Square : Shape { draw() }
Rectangle : Square { virtual draw() }

Shunda derivat Shapening chizilgani virtual emasligini ko'radi, shundan so'ng chaqiruv statik bo'ladi va Shape :: draw (yoki Base :: draw() aniqlanmagan) bo'ladi.

Hiyerarxiyada xuddi shu funktsiyani amalga oshirish uchun virtual bo'lmagan virtuallarni aralashtirishda eng yomon narsalar bo'lishi mumkin ... Odatda aralashtirishdan qochish kerak.

0
qo'shib qo'ydi

Birinchidan, derleyici nimani ko'rishi haqida o'ylab ko'ring. Belgilangan ko'rsatgich uchun ierarxiyani yuqoriga qarab kuzatib boradi va agar u xuddi shu usulni virtual deb topsa, u holda dinamik ish vaqti chaqiruvi chiqariladi. Agar u virtual ni ko'rmasa, u holda chiqadigan chaqiruv funktsiyaning "eng past" ta'rifiga mos keladigan turga (yoki birinchi turdagi toplardan yuqoriga qarab topilgan) to'g'ri keladi. Shunday qilib (agar sizda Square * p = new Rectangle() bo'lsa):

Shape { virtual draw() }
Square : Shape { virtual draw() }
Rectangle : Square { virtual draw() }

har bir narsa aniq, har doim virtual.

Agar shunday bo'lsa:

Shape { virtual draw() }
Square : Shape { draw() }
Rectangle : Square { virtual draw() }

shunda derleyici Shape-ning o'yini virtual ekanligini ko'rsa, unda qo'ng'iroq dinamik bo'ladi va Rectangle :: draw deb ataladi.

Agar shunday bo'lsa:

Shape { draw() }
Square : Shape { draw() }
Rectangle : Square { virtual draw() }

Shunda derivat Shapening chizilgani virtual emasligini ko'radi, shundan so'ng chaqiruv statik bo'ladi va Shape :: draw (yoki Base :: draw() aniqlanmagan) bo'ladi.

Hiyerarxiyada xuddi shu funktsiyani amalga oshirish uchun virtual bo'lmagan virtuallarni aralashtirishda eng yomon narsalar bo'lishi mumkin ... Odatda aralashtirishdan qochish kerak.

0
qo'shib qo'ydi

Birinchidan, derleyici nimani ko'rishi haqida o'ylab ko'ring. Belgilangan ko'rsatgich uchun ierarxiyani yuqoriga qarab kuzatib boradi va agar u xuddi shu usulni virtual deb topsa, u holda dinamik ish vaqti chaqiruvi chiqariladi. Agar u virtual ni ko'rmasa, u holda chiqadigan chaqiruv funktsiyaning "eng past" ta'rifiga mos keladigan turga (yoki birinchi turdagi toplardan yuqoriga qarab topilgan) to'g'ri keladi. Shunday qilib (agar sizda Square * p = new Rectangle() bo'lsa):

Shape { virtual draw() }
Square : Shape { virtual draw() }
Rectangle : Square { virtual draw() }

har bir narsa aniq, har doim virtual.

Agar shunday bo'lsa:

Shape { virtual draw() }
Square : Shape { draw() }
Rectangle : Square { virtual draw() }

shunda derleyici Shape-ning o'yini virtual ekanligini ko'rsa, unda qo'ng'iroq dinamik bo'ladi va Rectangle :: draw deb ataladi.

Agar shunday bo'lsa:

Shape { draw() }
Square : Shape { draw() }
Rectangle : Square { virtual draw() }

Shunda derivat Shapening chizilgani virtual emasligini ko'radi, shundan so'ng chaqiruv statik bo'ladi va Shape :: draw (yoki Base :: draw() aniqlanmagan) bo'ladi.

Hiyerarxiyada xuddi shu funktsiyani amalga oshirish uchun virtual bo'lmagan virtuallarni aralashtirishda eng yomon narsalar bo'lishi mumkin ... Odatda aralashtirishdan qochish kerak.

0
qo'shib qo'ydi

Xulosa

Statik ob'ektni yaratganingizda va uning vazifalaridan birini chaqirsangiz, siz o'sha sinfning funktsiyasi versiyasini olasiz. Derleyici, obyektning kompilyatsiya vaqtida qanday sinf ekanligini biladi.

Ob'ektga ko'rsatgich yoki mos yozuvlar yaratish va virtual bo'lmagan funktsiyani chaqirsangiz, kompilyator kompilyatsiya vaqtida turini bilishini ta'kidlaydi. Malumot aslida bir kichik sinfga tegishli bo'lsa, siz funktsiyaning superklass versiyasini olishingiz mumkin.

Ob'ektga ko'rsatgich yoki mos yozuvlar yaratish va virtual funktsiyani chaqirsangiz, derleyici aslida ish vaqti ob'ektidagi haqiqiy turini topadi va o'ziga xos funktsiya versiyasini chaqiradi. Agar juda muhim misol shundaki, agar siz ob'ektni asosiy sinfi ko'rsatkichi orqali yo'q qilishni istasangiz, asosiy tahdidni yo'q qilish uchun yiqituvchi virtual bo'lishi kerak, yoki siz halokatning to'g'ri versiyasini chaqira olmaysiz. Ko'pincha, bu subclass ajratadigan barcha dinamik xotiralarni ozod qilmaydi degan ma'noni anglatadi.

Misol

Maybe it would help to think about the practical purpose of virtual classes. The keyword wasn’t added just for its own sake! Let’s change the Misol slightly, to deal with circles, ellipses and shapes.

Bu yerda juda ko'p boilerplate bor, lekin faqat esda tuting: ellips - bu markazlashtirilgan nuqtalardan o'rtacha masofa va nuqta - bu fokuslar bir xil bo'lgan ellips.

#include 
#include 
#include 

using std::cout;

struct point {
  double x;
  double y;

  point(void) : x(0.0), y(0.0) {}
  point( const point& p ) : x(p.x), y(p.y) {}
  point( double a, double b ) : x(a), y(b) {}
};

double dist( const point& p, const point& q )
// Cartesian distance.
{
  const double dx = p.x - q.x;
  const double dy = p.y - q.y;

  return sqrt( dx*dx + dy*dy );
}

std::ostream& operator<< ( std::ostream& os, const point& p )
// Prints a point in the form "(1.4,2)".
{
  return os << '(' << p.x << ',' << p.y << ')';
}

class shape
{
  public:
    virtual bool is_inside( const point& p ) const = 0;//Pure virtual.

  protected:
   //Derived classes need to be able to call the default constructor, but no
   //actual objects of this class may be created.
    shape() { cout << "Created some kind of shape.\n"; }
   //Destructors of any superclass that might get extended should be virtual
   //in any real-world case, or any future subclass with a nontrivial
   //destructor will break:
    virtual ~shape() {}
};

// We can provide a default implementation for a pure virtual function!
bool shape::is_inside( const point& _ ) const
{
  cout << "By default, we'll say not inside.\n";
  return false;
}

class ellipse : public shape {
  public:
    ellipse( const point& p1, const point& p2, double avg_dist )
    : f1(p1), f2(p2), d(avg_dist)
    {
      cout << "Ellipse created with focuses " << f1 << " and " << f2
           << " and average distance " << d << " from them.\n";
    }

    bool is_inside( const point& p ) const
    {
      const double d1 = dist( p, f1 ), d2 = dist( p, f2 );
      const bool inside = d1+d2 <= d*2;

      cout << p << " has distance " << d1 << " from " << f1 << " and "
           << d2 << " from " << f2 << ", whose average is "
           << (inside ? "less than " : "not less than ") << d << ".\n";
      return inside;
    }

  protected://Not part of the public interface, but circle needs these.
    point f1; //The first focus.  For a circle, this is the center.
    point f2; //The other focus.  For a circle, this is the center, as well.
    double d; //The average distance to both focuses.  The radius of a circle.
};

class circle : public ellipse {
  public:
    circle( const point& center, double r )
    : ellipse( center, center, r )
    {
      cout << "Created a circle with center " << center << " and radius " << r
           << ".\n";
    }

   //Override the generic ellipse boundary test with a more efficient version:
    bool is_inside( const point& p ) const
    {
      const double d1 = dist(p, f1);
      const bool inside = d1 <= d;

      cout << p << " has distance " << d1 << " from " << f1 << ", which is "
           << (inside ? "less than " : "not less than ") << d << ".\n";
      return inside;
    }
};

int main(void)
{
  const circle c = circle( point(1,1), 1 );
  const shape& s = c;

 //These call circle::is_inside():
  c.is_inside(point(0,0));
  s.is_inside(point(0.5, 1.5));
  dynamic_cast(s).is_inside(point(0.5,0.5));

 //Call with static binding:
  static_cast(c).is_inside(point(0,0));
 //Explicitly call the base class function statically:
  c.ellipse::is_inside(point(0.5,0.5));
 //Explicitly call the ellipse version dynamically:
  dynamic_cast(s).ellipse::is_inside(point(0.5,0.5));

  return EXIT_SUCCESS;
}

Buning chiqishi:

Created some kind of shape.
Ellipse created with focuses (1,1) and (1,1) and average distance 1 from them.
Created a circle with center (1,1) and radius 1.
(0,0) has distance 1.41421 from (1,1), which is not less than 1.
(0.5,1.5) has distance 0.707107 from (1,1), which is less than 1.
(0.5,0.5) has distance 0.707107 from (1,1), which is less than 1.
(0,0) has distance 1.41421 from (1,1) and 1.41421 from (1,1), whose average is not less than 1.
(0.5,0.5) has distance 0.707107 from (1,1) and 0.707107 from (1,1), whose average is less than 1.
(0.5,0.5) has distance 0.707107 from (1,1) and 0.707107 from (1,1), whose average is less than 1.

Siz uni uch o'lchovga qanday o'zgartirishingiz mumkinligini, kartezyen polar koordinatlarga o'zgartirishni, uchburchak kabi boshqa shakllarni qo'shib qo'yishingiz yoki ellipslarning markaziy va eksa uchun ichki ko'rinishini o'zgartirishini bir lahzada o'ylashingiz mumkin.

Yon eslatma:

Dizaynerlar tez-tez fikr yuritadilar, chunki nuqta va masofa bilan doira aniqlanishi mumkin va ikkita nuqta va masofa bo'yicha ellipse ellips klassi daire ikkinchi fokus uchun a'zo qo'shadi. Bu faqatgina fil suyagi qasrning matematikasi bilan emas, balki xatodir.

If you take any algorithm for an ellipse (such as quickly computing its intersection with a line by using the equation for conic sections) and run it on a circle, it will still work. Here, we used the Misol of looking up whether a point is inside the circle twice as fast by using the knowledge that a circle has only one focus.

Lekin bu teskari ishlamaydi! Agar ellips kodi circle dan egalik qilsa va draw_fancy_circle (ellips (f1, f2, d)) ni ishlatmoqchi bo'lsangiz, ellips chizish kerak edi. Ellipslar doira klassi shartnomasini buzganligi sababli, doiralarni nazarda tutadigan har qanday kod, ularni qayta yozganingizcha, chizmacha chizilgan. Bu esa har qanday turdagi ob'ekt ustida ishlaydigan kodni yozib olish va ularni qayta ishlatish uchun nuqta o'chiradi.

kvadrat sinfining dikdörtgeni bo'lishi kerak bo'lgan munosabatlar oqibatlari o'quvchi uchun jismoniy mashqlar qilib qoldirildi.

0
qo'shib qo'ydi

Xulosa

Statik ob'ektni yaratganingizda va uning vazifalaridan birini chaqirsangiz, siz o'sha sinfning funktsiyasi versiyasini olasiz. Derleyici, obyektning kompilyatsiya vaqtida qanday sinf ekanligini biladi.

Ob'ektga ko'rsatgich yoki mos yozuvlar yaratish va virtual bo'lmagan funktsiyani chaqirsangiz, kompilyator kompilyatsiya vaqtida turini bilishini ta'kidlaydi. Malumot aslida bir kichik sinfga tegishli bo'lsa, siz funktsiyaning superklass versiyasini olishingiz mumkin.

Ob'ektga ko'rsatgich yoki mos yozuvlar yaratish va virtual funktsiyani chaqirsangiz, derleyici aslida ish vaqti ob'ektidagi haqiqiy turini topadi va o'ziga xos funktsiya versiyasini chaqiradi. Agar juda muhim misol shundaki, agar siz ob'ektni asosiy sinfi ko'rsatkichi orqali yo'q qilishni istasangiz, asosiy tahdidni yo'q qilish uchun yiqituvchi virtual bo'lishi kerak, yoki siz halokatning to'g'ri versiyasini chaqira olmaysiz. Ko'pincha, bu subclass ajratadigan barcha dinamik xotiralarni ozod qilmaydi degan ma'noni anglatadi.

Misol

Maybe it would help to think about the practical purpose of virtual classes. The keyword wasn’t added just for its own sake! Let’s change the Misol slightly, to deal with circles, ellipses and shapes.

Bu yerda juda ko'p boilerplate bor, lekin faqat esda tuting: ellips - bu markazlashtirilgan nuqtalardan o'rtacha masofa va nuqta - bu fokuslar bir xil bo'lgan ellips.

#include 
#include 
#include 

using std::cout;

struct point {
  double x;
  double y;

  point(void) : x(0.0), y(0.0) {}
  point( const point& p ) : x(p.x), y(p.y) {}
  point( double a, double b ) : x(a), y(b) {}
};

double dist( const point& p, const point& q )
// Cartesian distance.
{
  const double dx = p.x - q.x;
  const double dy = p.y - q.y;

  return sqrt( dx*dx + dy*dy );
}

std::ostream& operator<< ( std::ostream& os, const point& p )
// Prints a point in the form "(1.4,2)".
{
  return os << '(' << p.x << ',' << p.y << ')';
}

class shape
{
  public:
    virtual bool is_inside( const point& p ) const = 0;//Pure virtual.

  protected:
   //Derived classes need to be able to call the default constructor, but no
   //actual objects of this class may be created.
    shape() { cout << "Created some kind of shape.\n"; }
   //Destructors of any superclass that might get extended should be virtual
   //in any real-world case, or any future subclass with a nontrivial
   //destructor will break:
    virtual ~shape() {}
};

// We can provide a default implementation for a pure virtual function!
bool shape::is_inside( const point& _ ) const
{
  cout << "By default, we'll say not inside.\n";
  return false;
}

class ellipse : public shape {
  public:
    ellipse( const point& p1, const point& p2, double avg_dist )
    : f1(p1), f2(p2), d(avg_dist)
    {
      cout << "Ellipse created with focuses " << f1 << " and " << f2
           << " and average distance " << d << " from them.\n";
    }

    bool is_inside( const point& p ) const
    {
      const double d1 = dist( p, f1 ), d2 = dist( p, f2 );
      const bool inside = d1+d2 <= d*2;

      cout << p << " has distance " << d1 << " from " << f1 << " and "
           << d2 << " from " << f2 << ", whose average is "
           << (inside ? "less than " : "not less than ") << d << ".\n";
      return inside;
    }

  protected://Not part of the public interface, but circle needs these.
    point f1; //The first focus.  For a circle, this is the center.
    point f2; //The other focus.  For a circle, this is the center, as well.
    double d; //The average distance to both focuses.  The radius of a circle.
};

class circle : public ellipse {
  public:
    circle( const point& center, double r )
    : ellipse( center, center, r )
    {
      cout << "Created a circle with center " << center << " and radius " << r
           << ".\n";
    }

   //Override the generic ellipse boundary test with a more efficient version:
    bool is_inside( const point& p ) const
    {
      const double d1 = dist(p, f1);
      const bool inside = d1 <= d;

      cout << p << " has distance " << d1 << " from " << f1 << ", which is "
           << (inside ? "less than " : "not less than ") << d << ".\n";
      return inside;
    }
};

int main(void)
{
  const circle c = circle( point(1,1), 1 );
  const shape& s = c;

 //These call circle::is_inside():
  c.is_inside(point(0,0));
  s.is_inside(point(0.5, 1.5));
  dynamic_cast(s).is_inside(point(0.5,0.5));

 //Call with static binding:
  static_cast(c).is_inside(point(0,0));
 //Explicitly call the base class function statically:
  c.ellipse::is_inside(point(0.5,0.5));
 //Explicitly call the ellipse version dynamically:
  dynamic_cast(s).ellipse::is_inside(point(0.5,0.5));

  return EXIT_SUCCESS;
}

Buning chiqishi:

Created some kind of shape.
Ellipse created with focuses (1,1) and (1,1) and average distance 1 from them.
Created a circle with center (1,1) and radius 1.
(0,0) has distance 1.41421 from (1,1), which is not less than 1.
(0.5,1.5) has distance 0.707107 from (1,1), which is less than 1.
(0.5,0.5) has distance 0.707107 from (1,1), which is less than 1.
(0,0) has distance 1.41421 from (1,1) and 1.41421 from (1,1), whose average is not less than 1.
(0.5,0.5) has distance 0.707107 from (1,1) and 0.707107 from (1,1), whose average is less than 1.
(0.5,0.5) has distance 0.707107 from (1,1) and 0.707107 from (1,1), whose average is less than 1.

Siz uni uch o'lchovga qanday o'zgartirishingiz mumkinligini, kartezyen polar koordinatlarga o'zgartirishni, uchburchak kabi boshqa shakllarni qo'shib qo'yishingiz yoki ellipslarning markaziy va eksa uchun ichki ko'rinishini o'zgartirishini bir lahzada o'ylashingiz mumkin.

Yon eslatma:

Dizaynerlar tez-tez fikr yuritadilar, chunki nuqta va masofa bilan doira aniqlanishi mumkin va ikkita nuqta va masofa bo'yicha ellipse ellips klassi daire ikkinchi fokus uchun a'zo qo'shadi. Bu faqatgina fil suyagi qasrning matematikasi bilan emas, balki xatodir.

If you take any algorithm for an ellipse (such as quickly computing its intersection with a line by using the equation for conic sections) and run it on a circle, it will still work. Here, we used the Misol of looking up whether a point is inside the circle twice as fast by using the knowledge that a circle has only one focus.

Lekin bu teskari ishlamaydi! Agar ellips kodi circle dan egalik qilsa va draw_fancy_circle (ellips (f1, f2, d)) ni ishlatmoqchi bo'lsangiz, ellips chizish kerak edi. Ellipslar doira klassi shartnomasini buzganligi sababli, doiralarni nazarda tutadigan har qanday kod, ularni qayta yozganingizcha, chizmacha chizilgan. Bu esa har qanday turdagi ob'ekt ustida ishlaydigan kodni yozib olish va ularni qayta ishlatish uchun nuqta o'chiradi.

kvadrat sinfining dikdörtgeni bo'lishi kerak bo'lgan munosabatlar oqibatlari o'quvchi uchun jismoniy mashqlar qilib qoldirildi.

0
qo'shib qo'ydi

Xulosa

Statik ob'ektni yaratganingizda va uning vazifalaridan birini chaqirsangiz, siz o'sha sinfning funktsiyasi versiyasini olasiz. Derleyici, obyektning kompilyatsiya vaqtida qanday sinf ekanligini biladi.

Ob'ektga ko'rsatgich yoki mos yozuvlar yaratish va virtual bo'lmagan funktsiyani chaqirsangiz, kompilyator kompilyatsiya vaqtida turini bilishini ta'kidlaydi. Malumot aslida bir kichik sinfga tegishli bo'lsa, siz funktsiyaning superklass versiyasini olishingiz mumkin.

Ob'ektga ko'rsatgich yoki mos yozuvlar yaratish va virtual funktsiyani chaqirsangiz, derleyici aslida ish vaqti ob'ektidagi haqiqiy turini topadi va o'ziga xos funktsiya versiyasini chaqiradi. Agar juda muhim misol shundaki, agar siz ob'ektni asosiy sinfi ko'rsatkichi orqali yo'q qilishni istasangiz, asosiy tahdidni yo'q qilish uchun yiqituvchi virtual bo'lishi kerak, yoki siz halokatning to'g'ri versiyasini chaqira olmaysiz. Ko'pincha, bu subclass ajratadigan barcha dinamik xotiralarni ozod qilmaydi degan ma'noni anglatadi.

Misol

Maybe it would help to think about the practical purpose of virtual classes. The keyword wasn’t added just for its own sake! Let’s change the Misol slightly, to deal with circles, ellipses and shapes.

Bu yerda juda ko'p boilerplate bor, lekin faqat esda tuting: ellips - bu markazlashtirilgan nuqtalardan o'rtacha masofa va nuqta - bu fokuslar bir xil bo'lgan ellips.

#include 
#include 
#include 

using std::cout;

struct point {
  double x;
  double y;

  point(void) : x(0.0), y(0.0) {}
  point( const point& p ) : x(p.x), y(p.y) {}
  point( double a, double b ) : x(a), y(b) {}
};

double dist( const point& p, const point& q )
// Cartesian distance.
{
  const double dx = p.x - q.x;
  const double dy = p.y - q.y;

  return sqrt( dx*dx + dy*dy );
}

std::ostream& operator<< ( std::ostream& os, const point& p )
// Prints a point in the form "(1.4,2)".
{
  return os << '(' << p.x << ',' << p.y << ')';
}

class shape
{
  public:
    virtual bool is_inside( const point& p ) const = 0;//Pure virtual.

  protected:
   //Derived classes need to be able to call the default constructor, but no
   //actual objects of this class may be created.
    shape() { cout << "Created some kind of shape.\n"; }
   //Destructors of any superclass that might get extended should be virtual
   //in any real-world case, or any future subclass with a nontrivial
   //destructor will break:
    virtual ~shape() {}
};

// We can provide a default implementation for a pure virtual function!
bool shape::is_inside( const point& _ ) const
{
  cout << "By default, we'll say not inside.\n";
  return false;
}

class ellipse : public shape {
  public:
    ellipse( const point& p1, const point& p2, double avg_dist )
    : f1(p1), f2(p2), d(avg_dist)
    {
      cout << "Ellipse created with focuses " << f1 << " and " << f2
           << " and average distance " << d << " from them.\n";
    }

    bool is_inside( const point& p ) const
    {
      const double d1 = dist( p, f1 ), d2 = dist( p, f2 );
      const bool inside = d1+d2 <= d*2;

      cout << p << " has distance " << d1 << " from " << f1 << " and "
           << d2 << " from " << f2 << ", whose average is "
           << (inside ? "less than " : "not less than ") << d << ".\n";
      return inside;
    }

  protected://Not part of the public interface, but circle needs these.
    point f1; //The first focus.  For a circle, this is the center.
    point f2; //The other focus.  For a circle, this is the center, as well.
    double d; //The average distance to both focuses.  The radius of a circle.
};

class circle : public ellipse {
  public:
    circle( const point& center, double r )
    : ellipse( center, center, r )
    {
      cout << "Created a circle with center " << center << " and radius " << r
           << ".\n";
    }

   //Override the generic ellipse boundary test with a more efficient version:
    bool is_inside( const point& p ) const
    {
      const double d1 = dist(p, f1);
      const bool inside = d1 <= d;

      cout << p << " has distance " << d1 << " from " << f1 << ", which is "
           << (inside ? "less than " : "not less than ") << d << ".\n";
      return inside;
    }
};

int main(void)
{
  const circle c = circle( point(1,1), 1 );
  const shape& s = c;

 //These call circle::is_inside():
  c.is_inside(point(0,0));
  s.is_inside(point(0.5, 1.5));
  dynamic_cast(s).is_inside(point(0.5,0.5));

 //Call with static binding:
  static_cast(c).is_inside(point(0,0));
 //Explicitly call the base class function statically:
  c.ellipse::is_inside(point(0.5,0.5));
 //Explicitly call the ellipse version dynamically:
  dynamic_cast(s).ellipse::is_inside(point(0.5,0.5));

  return EXIT_SUCCESS;
}

Buning chiqishi:

Created some kind of shape.
Ellipse created with focuses (1,1) and (1,1) and average distance 1 from them.
Created a circle with center (1,1) and radius 1.
(0,0) has distance 1.41421 from (1,1), which is not less than 1.
(0.5,1.5) has distance 0.707107 from (1,1), which is less than 1.
(0.5,0.5) has distance 0.707107 from (1,1), which is less than 1.
(0,0) has distance 1.41421 from (1,1) and 1.41421 from (1,1), whose average is not less than 1.
(0.5,0.5) has distance 0.707107 from (1,1) and 0.707107 from (1,1), whose average is less than 1.
(0.5,0.5) has distance 0.707107 from (1,1) and 0.707107 from (1,1), whose average is less than 1.

Siz uni uch o'lchovga qanday o'zgartirishingiz mumkinligini, kartezyen polar koordinatlarga o'zgartirishni, uchburchak kabi boshqa shakllarni qo'shib qo'yishingiz yoki ellipslarning markaziy va eksa uchun ichki ko'rinishini o'zgartirishini bir lahzada o'ylashingiz mumkin.

Yon eslatma:

Dizaynerlar tez-tez fikr yuritadilar, chunki nuqta va masofa bilan doira aniqlanishi mumkin va ikkita nuqta va masofa bo'yicha ellipse ellips klassi daire ikkinchi fokus uchun a'zo qo'shadi. Bu faqatgina fil suyagi qasrning matematikasi bilan emas, balki xatodir.

If you take any algorithm for an ellipse (such as quickly computing its intersection with a line by using the equation for conic sections) and run it on a circle, it will still work. Here, we used the Misol of looking up whether a point is inside the circle twice as fast by using the knowledge that a circle has only one focus.

Lekin bu teskari ishlamaydi! Agar ellips kodi circle dan egalik qilsa va draw_fancy_circle (ellips (f1, f2, d)) ni ishlatmoqchi bo'lsangiz, ellips chizish kerak edi. Ellipslar doira klassi shartnomasini buzganligi sababli, doiralarni nazarda tutadigan har qanday kod, ularni qayta yozganingizcha, chizmacha chizilgan. Bu esa har qanday turdagi ob'ekt ustida ishlaydigan kodni yozib olish va ularni qayta ishlatish uchun nuqta o'chiradi.

kvadrat sinfining dikdörtgeni bo'lishi kerak bo'lgan munosabatlar oqibatlari o'quvchi uchun jismoniy mashqlar qilib qoldirildi.

0
qo'shib qo'ydi