Tworzenie obiektu z parametrami i niemożliwość późniejszego użycia metody z tej samej klasy na nim?

0

Tworzę program, w którym są różne figury. Mam je wyświetlić na ekranie np. ich koordynaty itp. Jest dużo dziedziczenia. Figura to klasa bazowa. Okrag, prostokat,trojkat i odcinek dziedziczy po klasie Figura. Punkt to klasa niezależna.
kompiluj programy w ten sposób (potem stworzę makefile)
g++ -c odcinek.cpp -Wall
g++ -c trojkat.cpp -Wall
g++ -c prostokat.cpp -Wall
g++ -c okrag.cpp -Wall
g++ -o figury.x figury.o trojkat.o prostokat.o okrag.o odcinek.o -Wall
Używam cppcheck do sprawdzenia kazdego pliku .cpp

Oto mój kod:
punkt.h

// Plik punkt.h
// Klasa Punkt do reprezentowania punktu na płaszczyźnie
#ifndef PUNKT_H
#define PUNKT_H

#include <iostream>
#include <cstdlib>

namespace MojeFigury {
    
    
    class Punkt {   
        double x, y;    // wspolrzedne x i y
    public:
        // Konstruktor
        Punkt(double a = 0, double b = 0): x(a), y(b) {}
        // Funkcja zwracająca współrzedną x
        double dajX() const{ return x; }
        // Funkcja zwracająca współrzedną y
        double dajY() const{ return y; }
        // Funkcja do przesuwania punktu na płaszczyźnie 
        // o wektor (dx,dy)
        void przesun(double dx, double dy);
        // Funkcja do skalowania punktu -pusta
        void skaluj(double s) {}
        // Przeładowanie operatorów relacyjnych: == i !=
        friend bool operator==(const Punkt&, const Punkt&);
        friend bool operator!=(const Punkt&, const Punkt&);
        // Przeładowanie operatorów strumieniowych: << i >>
        friend std::ostream& operator<<(std::ostream&,const Punkt&);
        friend std::istream& operator>>(std::istream&, Punkt&);
    }; // classPunkt
    
    //-----------Funkcje inline--------------------------
    // Funkcja do przesuwania punktu na płaszczyźnie
    // o wektor (dx,dy)
    inline void Punkt::przesun(double dx,  double dy) { 
        x += dx; 
        y += dy; 
    } 
    //===== Przeładowanie operatorów relacyjnych =========
    inline bool operator==(const Punkt& a, const Punkt& b)
    {
        return (a.x== b.x) && (a.y== b.y);
    }
    inline bool operator!=(const Punkt& a, const Punkt& b)
    {
        return (a.x!= b.x) || (a.y!= b.y);
    }
//===== Przeładowanie operatorów strumieniowych =======

inline std::ostream& operator<<(std::ostream& wy, const Punkt& p)
{
    wy << '(' << p.x<< ',' << p.y<< ')';
    return wy;
    
} //operator<<

inline std::istream& operator>>(std::istream& we, Punkt& p)
{
    char c;
    we >> c;
    if (c != '(') {
        std::cerr<< "Operator >>: Nieprawidlowyformat punktu!" 
                 << std::endl;
            std::exit(1);
    }
    we >> p.x;
    we >> c;
    if (c != ',') {
        std::cerr << "Operator >>:  Nieprawidlowyformat punktu!"                
<< std::endl;
        std::exit(2);
    }
    we >> p.y;
    we >> c;
    if (c != ')') {
        std::cerr << "Operator >>:  Nieprawidlowyformat punktu!" 
                  << std::endl;
        std::exit(3);
    }
    return we;
} // operator >>    
} // namespace MojeFigury
#endif // PUNKT_H

figura.h

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#include <string>
#include <iostream>

#ifndef FIGURA_H 
#define FIGURA_H

using namespace std;

namespace MojeFigury {
    
    class Figura { // Klasa abstrakcyjna Figura
        public:
            // Czysto wirtualny destruktor
            virtual~Figura() = 0;
            // Funkcja czysto wirtualna do rysowania figur
            virtual void rysuj(void) = 0;
            // Funkcja czysto wirtualna do przesuwania figur
            virtual void przesun(double, double) = 0;
            // Funkcja czysto wirtualna do skalowania figur
            virtual void skaluj(double) = 0;
            
// Zagnieżdżonaklasa do obsługi wyjątków dla funkcji skaluj
        class ZlaSkala{
            double s;         // skala 
            string gdzie;    // miejsce zgłoszenia wyjątku: klasa
            
        public:
            // Konstruktor
            ZlaSkala(double x=0,std::string opis=""): s(x),gdzie(opis){}
            // Zwróć skalę
            float dajSkale() const{ return s; }
            // Komunikat
            void Komunikat() const{
                std::cerr<< "\nWyjatek w funkcji skaluj dla klasy: " 
                    << gdzie << ": zla skala s = " << s << std::endl;
            }
        }; // classZlaSkala
        
    }; // classFigur
    // Definicja czysto wirtualnego destruktora (tu: pusty).
    //Obowiązkowa!!! 
    //-> Na wypadek gdyby jakaś klasa pochodna
    //    nie zdefiniowała własnego destruktora!
    //    (Wtedy stałaby się klasą abstrakcyjną,
    //    bo destruktor zadeklarowaliśmy jako
    //    czysto wirtualny.)
    inline Figura::~Figura() {}
    
} // namespace MojeFigury

#endif // FIGURA_H

odcinek.h

// Plik odcinek.h
// Definicja klasy Odcinek do reprezentowania odcinka
// Klasa Odcinek dziedziczy po klasie abstrakcyjnej Figura
#ifndef ODCINEK_H
#define ODCINEK_H

#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt

namespace MojeFigury {
    
    // Klasa Odcinek – dziedziczy po klasie Figura
    class Odcinek: public Figura {  
        Punkt poczatek, koniec; // Punkty początkowy i końcowy
        
        public:
            //Konstruktor, w tym domyślny (przykładowy)
            Odcinek(Punkt a = Punkt(0,1), Punkt b = Punkt(0,0)); 
            // Funkcja do rysowania odcinka w oknie
            void rysuj(void);
            // Funkcja do przesuwania odcinka
            void przesun(double, double);
            // Funkcja do skalowania odcinka
            void skaluj(double);
            // Klasa do obsługi wyjątków
            class ZleKonce {
                    Punkt p, k;  // konce odcinka
                public:
                    // Konstruktor
                    ZleKonce(Punkt a, Punkt b): p(a), k(b) {}
                    // Zwróć końce odcinka
                    void dajKonce(Punkt& a, Punkt& b) const
                    { a = p; b = k; }
                    // Komunikat
                    void Komunikat() const
                    { 
                        std::cerr<< "\n* Wyjatek: class Odcinek: poczatek"
                                 << p << " = koniec" << k << std::endl;
                        
                    }
            }; // class ZleKonce
            
    }; // class Odcinek
    
    // Definicja konstruktora
    inline Odcinek::Odcinek(Punkt a, Punkt b) {
        if (a != b) {
            poczatek = a; koniec = b;
        }
        // Rzucanie wyjątku gdy złe końce, 
        // tzn. poczatek = koniec
        else 
            throw Odcinek::ZleKonce(a, b); 
    }
    
} // namespaceMojeFigury

#endif

odcinek.cpp

// Plik odcinek.cpp
// Implementacja metod klasy Odcinek
#include "odcinek.h" 

namespace MojeFigury {
    // Funkcja do rysowania odcinka
    void Odcinek::rysuj(){
        std::cout << "Rysuje odcinek o poczatku P" << poczatek
                  << " i koncu K" << koniec << std::endl;
    }
    // Funkcja do przesuwania odcinka o wektor (dx,dy)
    void Odcinek::przesun(double dx, double dy) { 
        koniec.przesun(dx,dy);
        poczatek.przesun(dx,dy);
    }
    // Funkcja do skalowania odcinka
    void Odcinek::skaluj(double s) {
        if(s <= 0) throw Figura::ZlaSkala(s, "Odcinek"); 
        double x0 = poczatek.dajX();
        double delx= koniec.dajX() -x0;
        double y0 = poczatek.dajY();
        double dely= koniec.dajY() -y0;
        // Nowe współrzędne końca -po przeskalowaniu
        double x = x0 + s*delx;
        double y = y0 + s*dely;
        koniec = Punkt(x,y);
    }
    
} // namespace MojeFigury

Plik główny

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#include <string>
#include <iostream>
#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt
#include "odcinek.h"  // Plik nagłówkowy dla klasy Figura
#include "trojkat.h"   // Plik nagłówkowy dla klasy Punkt
#include "prostokat.h"  // Plik nagłówkowy dla klasy Figura
#include "okrag.h"   // Plik nagłówkowy dla klasy Punkt

using namespace std;

using namespace MojeFigury;

int main() {
    try {
        // deklaracje, definicje, instrukcje, wywołania funkcji, ...
    } catch(Odcinek::ZleKonce& zk) {
        // obsługa wyjątku dla konstruktora odcinka, np. 
        zk.Komunikat();
    } catch(Trojkat::ZleWierzcholki& zw) {
        // obsługa wyjątku dla konstruktora trójkąta
        zw.Komunikat();
    } catch(Prostokat::ZleBoki& zb) {
        // obsługa wyjątku dla konstruktora prostokąta
        zb.Komunikat();
    } catch(Okrag::ZlyPromien& zp) {
        // obsługa wyjątku dla konstruktora okręgu
        zp.Komunikat();
    } catch(Figura::ZlaSkala& zs) {
        // obsługa wyjątku dla metody skaluj()
        zs.Komunikat();
    } catch(...) { // domyślna obsługa wyjątków
      cerr << "Nieznany wyjątek" << endl;
      exit(1);
    }
    Odcinek odc1(Punkt c = Punkt(0,1), Punkt d = Punkt(0,3));
    odc1.rysuj();


    
    return 0;
    
}
   

Wyskakuje mi w takim przypadku błąd przy g++ -c figury.cpp -Wall

figury.cpp: In function ‘int main()’:
figury.cpp:39:10: error: request for member ‘rysuj’ in ‘odc1’, which is of non-class type ‘MojeFigury::Odcinek(MojeFigury::Punkt, MojeFigury::Punkt)’
     odc1.rysuj();

Natomiast gdy podaje obiekt bez parametrów wszystko jest super

    Odcinek odc1;
    odc1.rysuj();

, no ale chyba pasuje stworzyć jakiś odcinek z konkretnymi współrzędnymi.

Wiecie w czym może być problem?

2
    Odcinek odc1(Punkt c = Punkt(0,1), Punkt d = Punkt(0,3));

To jest deklaracja funkcji o nazwie odc1, która przyjmuje argument o nazwie c typu Punkt, z wartością domyślną Punkt(0,1), argument o nazwie d typu Punkt z wartością domyślną Punkt(0,3), zwracającą zmienną typu Odcinek.

Raczej miał(a|eś) na myśli

    Odcinek odc1(Punkt(0,1), Punkt(0,3));
2

Dodam, zejdź z konstruktora z defaultami.

 Odcinek(Punkt a = Punkt(0,1), Punkt b = Punkt(0,0)); 

W tym przypadku nie odwzorowuje to żadnej realnej sytuacji "z życia"

Pokaprysił bym nad klasami "jakby excetion" jak ZleKonce i sąsiednie
a) nie powinna drukować osobiście

b) dziedziczyć z std::exception i implementować wirtualne what()

c) ewentualnie nie bałbym się rzucać fabryczne invalid_argument czy generalnie z grupy 'logical'
https://en.cppreference.com/w/cpp/error/exception
Programiści C# mnie nauczyli: nie powoływać zbyt dużo klas wyjątków, jeśli i tak nie masz zamiaru ich seletywnie łapać (opozycja do programistów Javy, czasem przesadzają)

0

Pojawił się jeszcze jeden problem. Mianowicie, gdy tworzę obiekt, to program używa wartości konstruktora domyślnego dla punktu z pliku punkt.h, zamiast dla tego z mojej właściwej figury. O dziwo. W przypadku trójkąta dobrze tworzy, natomiast w przypadku prostokąta źle, np.

    Prostokat pros1(Punkt(3,1), 3, 5);
    pros1.rysuj();

Zamiast

Rysuje prostokat o poczatku P(3,1) dlugosci pierwszego boku 3 i dlugosci drugiego boku 5.

Mam

Rysuje prostokat o poczatku P(0,0) dlugosci pierwszego boku 3 i dlugosci drugiego boku 5.

Taki sam mam problem z tworzeniem wartości dla obiektów typu okręg, natomiast trójkąt robi dobrze.

Oto jeszcze moje dodatkowe pliki
prostokat.h

// Plik prostokat.h
// Definicja klasy Prostokat do reprezentowania odcinka
// Klasa Prostokat dziedziczy po klasie abstrakcyjnej Figura
#ifndef PROSTOKAT_H
#define PROSTOKAT_H

#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt

namespace MojeFigury {
    
    // Klasa Prostokat – dziedziczy po klasie Figura
    class Prostokat: public Figura { 
        Punkt poczatek;
        double a;
        double b;
    public:
            //Konstruktor, w tym domyślny (przykładowy)
            Prostokat(Punkt p = Punkt(0,1), double bok1 = 2, double bok2 = 3); 
            // Funkcja do rysowania odcinka w oknie
            void rysuj(void);
            // Funkcja do przesuwania odcinka
            void przesun(double, double);
            // Funkcja do skalowania odcinka
            void skaluj(double);
            // Klasa do obsługi wyjątków
        class ZleBoki {
                double r, s;  // wierzcholki boki
            public:
                // Konstruktor
                ZleBoki(double bok1, double bok2): r(bok1), s(bok2) {}
                // Zwróć boki prostokata
                void dajBoki(double &bok1, double &bok2) const
                { bok1 = r; bok2 = s;}
                // Komunikat
                void Komunikat() const
                { 
                    std::cerr<< "\n* Wyjatek: class Prostokat: Boki są ujemne. Pierwszy: "
                                << r << ", drugi: " << s << "." <<std::endl;
                
                }
        }; // class ZleBoki
    };
    inline Prostokat::Prostokat(Punkt p, double bok1, double bok2) {
        if ((bok1 >= 0) && (bok2 >= 0)) {
            a = bok1; b = bok2;
        }
        // Rzucanie wyjątku gdy złe końce, 
        // tzn. bok1 i bok2 < 0
        else 
            throw Prostokat::ZleBoki(bok1, bok2); 
    }
}
#endif

prostokat.cpp

// Plik prostokat.cpp
// Implementacja metod klasy Prostokat
#include "prostokat.h" 

namespace MojeFigury {
        // Funkcja do rysowania prostokata
    void Prostokat::rysuj(){
        std::cout << "Rysuje prostokat o poczatku P" << poczatek
                  << " dlugosci pierwszego boku " << a << " i dlugosci drugiego boku " << b << "." << std::endl;
    }
    // Funkcja do przesuwania prostokata o wektor (dx,dy)
    void Prostokat::przesun(double dx, double dy) { 
        poczatek.przesun(dx,dy);
    }
    // Funkcja do skalowania odcinka
    void Prostokat::skaluj(double s) {
        if(s <= 0) throw Figura::ZlaSkala(s, "Prostokat"); 
        a = s*a;
        b = s*b;
    }
    
}

okrag.h

// Plik okrag.h
// Definicja klasy Okrag do reprezentowania odcinka
// Klasa Okrag dziedziczy po klasie abstrakcyjnej Figura
#ifndef OKRAG_H
#define OKRAG_H

#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt

namespace MojeFigury {
    
    // Klasa Okrag – dziedziczy po klasie Figura
    class Okrag: public Figura { 
        Punkt srodek;
        double r;
    public:
            //Konstruktor, w tym domyślny (przykładowy)
            Okrag(Punkt a = Punkt(3,4), double promien = 3); 
            // Funkcja do rysowania odcinka w oknie
            void rysuj(void);
            // Funkcja do przesuwania odcinka
            void przesun(double, double);
            // Funkcja do skalowania odcinka
            void skaluj(double);
            // Klasa do obsługi wyjątków
        class ZlyPromien {
                double s;  // promien okregu
            public:
                // Konstruktor
                ZlyPromien(double promien): s(promien) {}
                // Zwróć promien okregu
                void dajPromien(double &promien) const
                { promien = s;}
                // Komunikat
                void Komunikat() const
                { 
                    std::cerr<< "\n* Wyjatek: class Okrag: Promien jest ujemny. Wynosi: " << s << "." <<std::endl;
                
                }
        }; // class ZlyPromien
    };
    inline Okrag::Okrag(Punkt p, double promien) {
        if (promien > 0) {
            r = promien;
        }
        // Rzucanie wyjątku gdy promien ujemny, 
        // tzn. r < 0
        else 
            throw Okrag::ZlyPromien(promien); 
    }
}
#endif

okrag.cpp

// Plik okrag .cpp
// Implementacja metod klasy okrag
#include "okrag.h"

namespace MojeFigury {
        // Funkcja do rysowania okregu
    void Okrag::rysuj(){
        std::cout << "Rysuje okrag o srodku P" << srodek
                  << " i promieniu r " << r << "." << std::endl;
    }
    // Funkcja do przesuwania okregu o wektor (dx,dy)
    void Okrag::przesun(double dx, double dy) { 
        srodek.przesun(dx,dy);
    }
    // Funkcja do skalowania okregu
    void Okrag::skaluj(double s) {
        if(s <= 0) throw Figura::ZlaSkala(s, "Okrag"); 
        r = s*r;
    }
    
}

Natomiast trójkąt działa elegancko
trojkat.h

// Plik trojkat.h
// Definicja klasy Trojkat do reprezentowania odcinka
// Klasa Trojkat dziedziczy po klasie abstrakcyjnej Figura
#ifndef TROJKAT_H
#define TROJKAT_H

#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt

namespace MojeFigury {
    
    // Klasa Trojkat – dziedziczy po klasie Figura
    class Trojkat: public Figura { 
        Punkt w1, w2, w3;
        
        public:
            //Konstruktor, w tym domyślny (przykładowy)
            Trojkat(Punkt a = Punkt(0,1), Punkt b = Punkt(0,0), Punkt c = Punkt(1,1)); 
            // Funkcja do rysowania odcinka w oknie
            void rysuj(void);
            // Funkcja do przesuwania odcinka
            void przesun(double, double);
            // Funkcja do skalowania odcinka
            void skaluj(double);
            // Klasa do obsługi wyjątków
        class ZleWierzcholki {
                Punkt p, r, s;  // wierzcholki trojkata
            public:
                // Konstruktor
                ZleWierzcholki(Punkt a, Punkt b, Punkt c): p(a), r(b), s(c) {}
                // Zwróć wierzcholki trojkata
                void dajWierzcholki(Punkt& a, Punkt& b, Punkt& c) const
                { a = p; b = r; c = s;}
                // Komunikat
                void Komunikat() const
                { 
                    std::cerr<< "\n* Wyjatek: class Trojkat: wierzcholki sie nakladaja. Pierwszy: "
                                << p << ", drugi: " << r << ", trzeci: " << s << "." <<std::endl;
                
                }
        }; // class ZleWierzcholki
    };
    
    // Definicja konstruktora
    inline Trojkat::Trojkat(Punkt a, Punkt b, Punkt c) {
        if ((a != b) && (b != c) && (a != c)) {
            w1 = a; w2 = b; w3 = c;
        }
        // Rzucanie wyjątku gdy złe wierzcholki, 
        // tzn. w1 = w2 = w3
        else 
            throw Trojkat::ZleWierzcholki(a, b, c); 
    }
    
}
#endif

trojkat.cpp

// Plik trojkat.cpp
// Implementacja metod klasy trojkat
#include "trojkat.h"

namespace MojeFigury {
    void Trojkat::rysuj(){
        std::cout << "Rysuje trojkat o pierwszym wierzcholku " << w1
                  << " drugim wierzcholku " << w2 << " trzecim wierzcholku " << w3 << "." << std::endl;
    }
    // Funkcja do przesuwania odcinka o wektor (dx,dy)
    void Trojkat::przesun(double dx, double dy) { 
        w1.przesun(dx,dy);
        w2.przesun(dx,dy);
        w3.przesun(dx,dy);
    }
    // Funkcja do skalowania odcinka
    void Trojkat::skaluj(double s) {
        if(s <= 0) throw Figura::ZlaSkala(s, "Trojkat"); 
        double x0 = w1.dajX();
        double delx= w2.dajX() -x0;
        double y0 = w1.dajY();
        double dely= w2.dajY() -y0;
        double x1 = w1.dajX();
        double delx1= w3.dajX() -x1;
        double y1 = w1.dajY();
        double dely1= w3.dajY() -y1;
        // Nowe współrzędne końca -po przeskalowaniu
        double x = x0 + s*delx;
        double y = y0 + s*dely;
        w2 = Punkt(x,y);
        double xv2 = x1 + s*delx1;
        double yv2 = y1 + s*dely1;
        w3 = Punkt(xv2,yv2);
        
    }
    
}

0
    inline Prostokat::Prostokat(Punkt p, double bok1, double bok2) {
        if ((bok1 >= 0) && (bok2 >= 0)) {
            a = bok1; b = bok2;
        }
        // Rzucanie wyjątku gdy złe końce, 
        // tzn. bok1 i bok2 < 0
        else 
            throw Prostokat::ZleBoki(bok1, bok2); 
    }

Zadanie dla uważnych: wskaż, gdzie w tym kodzie używasz parametru p aby go przypisać do zmiennej klasy.

0
kq napisał(a):
    inline Prostokat::Prostokat(Punkt p, double bok1, double bok2) {
        if ((bok1 >= 0) && (bok2 >= 0)) {
            a = bok1; b = bok2;
        }
        // Rzucanie wyjątku gdy złe końce, 
        // tzn. bok1 i bok2 < 0
        else 
            throw Prostokat::ZleBoki(bok1, bok2); 
    }

Zadanie dla uważnych: wskaż, gdzie w tym kodzie używasz parametru p aby go przypisać do zmiennej klasy.

Wielkie dzięki, no masz rację. Działa teraz super, bardzo szybko to znalazłeś, teraz mój kod wygląda tak:

    inline Prostokat::Prostokat(Punkt p, double bok1, double bok2) {
        if ((bok1 >= 0) && (bok2 >= 0)) {
            a = bok1; b = bok2;
            poczatek = p;
        }
    inline Okrag::Okrag(Punkt a, double promien) {
        if (promien > 0) {
            r = promien;
            srodek = a;
        }
3

C++11 używaj zawsze brace initialization
Jedyny wyjątek to, gdy należy używać starej inicjalizacji to, gdy intialization_list stoi na drodze.

0

Spotkałem niestety kolejny problem przy kolejnym etapie ćwiczenia. Oto część kodu, którą dołożyłem do figury.cpp

    Figura* wd[10];


    wd[0] = odc1;
    wd[1] = troj1;
    wd[2] = pros1;
    wd[3] = okr1;

    for(int i = 0; i <= 3; i++ )
    {
        wd[i].rysuj();
    }
    delete [] wd;
    wd = 0;

Polecenie jakie teraz mam wykonać "Utworzyć tablicę wskaźników dla klasy Figura, wstawić do niej adresy utworzonych obiektów figur i zademonstrować polimorfizm wykonując operacje na figurach przy użyciu tej tablicy, najlepiej w pętli for."

wyskakujące błędy

figury.cpp:94:13: error: cannot convert ‘MojeFigury::Odcinek’ to ‘MojeFigury::Figura*’ in assignment
     wd[0] = odc1;
             ^~~~
figury.cpp:95:13: error: cannot convert ‘MojeFigury::Trojkat’ to ‘MojeFigury::Figura*’ in assignment
     wd[1] = troj1;
             ^~~~~
figury.cpp:96:13: error: cannot convert ‘MojeFigury::Prostokat’ to ‘MojeFigury::Figura*’ in assignment
     wd[2] = pros1;
             ^~~~~
figury.cpp:97:13: error: cannot convert ‘MojeFigury::Okrag’ to ‘MojeFigury::Figura*’ in assignment
     wd[3] = okr1;
             ^~~~
figury.cpp:103:15: warning: deleting array ‘wd’
     delete [] wd;
               ^~
figury.cpp:104:10: error: incompatible types in assignment of ‘int’ to ‘MojeFigury::Figura* [10]’
     wd = 0;

Ostatnie dwa ostrzeżenia - no to przecież trzeba usunąć tablicę, żeby zwolnić pamięc no nie? No i wskaźnik też należy wyzerować tylko jak to zrobić, bo on wskazuje na jakieś tam obiekty klasy, a nie na liczby. No i główny problem. Nie wiem jak mam to zrobić. Dla klasy Figura bazowej poutwarzałem kilka obiektów ale z klas dziedziczących z niej, bo w niej są tylko funkcje wirtualne itp. Ma ktoś pomysł jak rozwiązać ten problem?

Cały kod figury.cpp

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#include <string>
#include <iostream>
#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt
#include "odcinek.h"  // Plik nagłówkowy dla klasy Figura
#include "trojkat.h"   // Plik nagłówkowy dla klasy Punkt
#include "prostokat.h"  // Plik nagłówkowy dla klasy Figura
#include "okrag.h"   // Plik nagłówkowy dla klasy Punkt
#include "figury.h"


using namespace std;

using namespace MojeFigury;

int main() {
    try {
        // deklaracje, definicje, instrukcje, wywołania funkcji, ...
    } catch(Odcinek::ZleKonce& zk) {
        // obsługa wyjątku dla konstruktora odcinka, np. 
        zk.Komunikat();
    } catch(Trojkat::ZleWierzcholki& zw) {
        // obsługa wyjątku dla konstruktora trójkąta
        zw.Komunikat();
    } catch(Prostokat::ZleBoki& zb) {
        // obsługa wyjątku dla konstruktora prostokąta
        zb.Komunikat();
    } catch(Okrag::ZlyPromien& zp) {
        // obsługa wyjątku dla konstruktora okręgu
        zp.Komunikat();
    } catch(Figura::ZlaSkala& zs) {
        // obsługa wyjątku dla metody skaluj()
        zs.Komunikat();
    } catch(...) { // domyślna obsługa wyjątków
      cerr << "Nieznany wyjątek" << endl;
      exit(1);
    }
    Odcinek odc1(Punkt(0,1), Punkt(0,3));
    odc1.rysuj();
    odc1.przesun(2,5);
    odc1.skaluj(3);
    odc1.rysuj();
    Odcinek* odc2 = new Odcinek(Punkt(2,6), Punkt(8,2));
    odc2->rysuj();
    odc2->przesun(4,1);
    odc2->skaluj(2);
    odc2->rysuj();
    delete odc2; odc2 = 0; 
    
    Trojkat troj1(Punkt(0,1), Punkt(0,3), Punkt(1,3));
    troj1.rysuj();
    troj1.przesun(4,1);
    troj1.skaluj(2);
    troj1.rysuj();
    Trojkat* troj2 = new Trojkat(Punkt(1,2), Punkt(4,1), Punkt(5,5));
    troj2->rysuj();
    troj2->przesun(4,1);
    troj2->rysuj();
    troj2->skaluj(2);
    troj2->rysuj();
    delete troj2; troj2 = 0; 
    
    Prostokat pros1(Punkt(3,1), 3, 5);
    pros1.rysuj();
    pros1.przesun(7,3);
    pros1.skaluj(7);
    pros1.rysuj();
    Prostokat* pros2 = new Prostokat(Punkt(1,2), 5, 2);
    pros2->rysuj();
    pros2->przesun(5,6);
    pros2->skaluj(5);
    pros2->rysuj();    
    delete pros2; pros2 = 0; 

    Okrag okr1(Punkt(4,3), 25);
    okr1.rysuj();
    okr1.przesun(11,7);
    okr1.rysuj();
    okr1.skaluj(4);  
    okr1.rysuj();
    Okrag* okr2 = new Okrag(Punkt(15,3), 5);
    okr2->rysuj();
    okr2->przesun(4,2);
    okr2->skaluj(14);  
    okr2->rysuj();
    delete okr2; okr2 = 0; 
    
    
    Figura* wd[10];


    wd[0] = odc1;
    wd[1] = troj1;
    wd[2] = pros1;
    wd[3] = okr1;

    for(int i = 0; i <= 3; i++ )
    {
        wd[i].rysuj();
    }
    delete [] wd;
    wd = 0;
    
    return 0;
    
}
    

2

wd[0] = odc1;
Próbujesz tutaj przypisać stworzony obiekt do wskaźnika. Masz przypisać adres obiektu, czyli &odc1.

wd = 0;
Tego nie możesz zrobić, bo to jest tablica wskaźników, która jest sztywnym miejscem w pamięci (sama nazwa tablicy wskazuje na pierwszy jej element, ale nie można przypisać do niej czegoś innego, co najwyżej do jej elementów).

A do zerowania wskaźników lepiej używaj nullptr zamiast 0.

0

Nie spodziewałem się, że tak daleko zajdę, dzięki waszym radom. Otrzymałem teraz takie zadanie "Przy użyciu szablonów utworzyć kontener w postaci tablicy z kontrolą zakresów i rozmiarem przekazywanym przez argument szablonu (patrz wykład 17), następnie przy jego pomocy zdefiniować tablicę wskaźników dla klasy Figura i zademonstrować polimorfizm jak wyżej.

Zrobiłem odpowiedni szablon
szablon.h

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#ifndef FIGURY_H
#define FIGURY_H
#include <iostream>
#include <cstdlib>
#include <string>
#include <iostream>
#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt
#include "odcinek.h"  // Plik nagłówkowy dla klasy Figura
#include "trojkat.h"   // Plik nagłówkowy dla klasy Punkt
#include "prostokat.h"  // Plik nagłówkowy dla klasy Figura
#include "okrag.h"   // Plik nagłówkowy dla klasy Punkt

using namespace std;

using namespace MojeFigury;

template <typename T> // definicja szablonu klasy dla typu T
class SzablonFigury {       // tablica z kontrolą zakresu indeksów
    enum { n = 10000 }; // -> C++11 pozwala na: static  const n = 100;
    T* w[n];
    public:T& operator[](int i) { // operator indeksowania tablicy
        if(i < 0 || i >= n) {std::cerr<< "Indeks poza zakresem: " << i << std::endl;
            std::exit(1);
            
        }
        return *w[n];
        
    } // operator[]
}; // classWektor
#endif

Przy kompilacji wszystko jest ok. Musiałem go przerobić, bo wcześniej pracował on na liczbach przy indeksowaniu, a teraz robię indeksy dla kolejnych figur z klasy Figura (klasa abstrakcyjna).

Gdy uruchamiam program mam naruszenie ochrony pamięci. Postanowiłem sprawdzić cppcheckiem, co jest grane. Wyskoczyło mi takie coś:

szablon.h:29:18: error: Array 'w[10000]' accessed at index 10000, which is out of bounds. [arrayIndexOutOfBounds]
        return *w[n];
                 ^

Tutaj kod, który wykorzystuje szablon, a potem cały kod

    SzablonFigury <Figura> wi;       
    for (int i = 0; i < 7; i++) {
        wi[i].rysuj();
    }

Wiecie może w czym może być problem.
Cały kod programu:

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#include <string>
#include <iostream>
#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt
#include "odcinek.h"  // Plik nagłówkowy dla klasy Figura
#include "trojkat.h"   // Plik nagłówkowy dla klasy Punkt
#include "prostokat.h"  // Plik nagłówkowy dla klasy Figura
#include "okrag.h"   // Plik nagłówkowy dla klasy Punkt
#include "szablon.h"


using namespace std;

using namespace MojeFigury;

int main() {
    try {
        // deklaracje, definicje, instrukcje, wywołania funkcji, ...
    } catch(Odcinek::ZleKonce& zk) {
        // obsługa wyjątku dla konstruktora odcinka, np. 
        zk.Komunikat();
    } catch(Trojkat::ZleWierzcholki& zw) {
        // obsługa wyjątku dla konstruktora trójkąta
        zw.Komunikat();
    } catch(Prostokat::ZleBoki& zb) {
        // obsługa wyjątku dla konstruktora prostokąta
        zb.Komunikat();
    } catch(Okrag::ZlyPromien& zp) {
        // obsługa wyjątku dla konstruktora okręgu
        zp.Komunikat();
    } catch(Figura::ZlaSkala& zs) {
        // obsługa wyjątku dla metody skaluj()
        zs.Komunikat();
    } catch(...) { // domyślna obsługa wyjątków
      cerr << "Nieznany wyjątek" << endl;
      exit(1);
    }
    Odcinek odc1(Punkt(0,1), Punkt(0,3));
    odc1.rysuj();
    odc1.przesun(2,5);
    odc1.skaluj(3);
    odc1.rysuj();
    Odcinek* odc2 = new Odcinek(Punkt(2,6), Punkt(8,2));
    odc2->rysuj();
    odc2->przesun(4,1);
    odc2->skaluj(2);
    odc2->rysuj();
    
    Trojkat troj1(Punkt(0,1), Punkt(0,3), Punkt(1,3));
    troj1.rysuj();
    troj1.przesun(4,1);
    troj1.skaluj(2);
    troj1.rysuj();
    Trojkat* troj2 = new Trojkat(Punkt(1,2), Punkt(4,1), Punkt(5,5));
    troj2->rysuj();
    troj2->przesun(4,1);
    troj2->rysuj();
    troj2->skaluj(2);
    troj2->rysuj();
     
    
    Prostokat pros1(Punkt(3,1), 3, 5);
    pros1.rysuj();
    pros1.przesun(7,3);
    pros1.skaluj(7);
    pros1.rysuj();
    Prostokat* pros2 = new Prostokat(Punkt(1,2), 5, 2);
    pros2->rysuj();
    pros2->przesun(5,6);
    pros2->skaluj(5);
    pros2->rysuj();    

    Okrag okr1(Punkt(4,3), 25);
    okr1.rysuj();
    okr1.przesun(11,7);
    okr1.rysuj();
    okr1.skaluj(4);  
    okr1.rysuj();
    Okrag* okr2 = new Okrag(Punkt(15,3), 5);
    okr2->rysuj();
    okr2->przesun(4,2);
    okr2->skaluj(14);  
    okr2->rysuj();

    cout << "\n\n\n\n" << endl;

    
    
    Figura* wd[10];


    wd[0] = &odc1;
    wd[1] = &troj1;
    wd[2] = &pros1;
    wd[3] = &okr1;
    wd[4] = odc2;
    wd[5] = troj2;
    wd[6] = pros2;
    wd[7] = okr2;

    for(int i = 0; i <= 7; i++ )
    {
        wd[i]->rysuj();
    }
    cout << "\n\n\n\n" << endl;
    
    
    SzablonFigury <Figura> wi;       
    for (int i = 0; i < 7; i++) {
        wi[i].rysuj();
    }


    delete odc2; odc2 = 0; 
    delete troj2; troj2 = 0;
    delete pros2; pros2 = 0;
    delete okr2; okr2 = 0;
    
    return 0;
    
}
    

1
        if(i < 0 || i >= n) {std::cerr<< "Indeks poza zakresem: " << i << std::endl;

Jeśli i == n, to będziesz miał dostęp poza zakresem

0

Dzięki za radę. Zmodyfikowałem plik i już błąd w cppchecku nie występuje, ale dalej mam przy uruchamianiu naruszenie ochrony pamięci. Ciekawe czemu?
szablon.h

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#ifndef FIGURY_H
#define FIGURY_H
#include <iostream>
#include <cstdlib>
#include <string>
#include <iostream>
#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt
#include "odcinek.h"  // Plik nagłówkowy dla klasy Figura
#include "trojkat.h"   // Plik nagłówkowy dla klasy Punkt
#include "prostokat.h"  // Plik nagłówkowy dla klasy Figura
#include "okrag.h"   // Plik nagłówkowy dla klasy Punkt

using namespace std;

using namespace MojeFigury;

template <typename T> // definicja szablonu klasy dla typu T
class SzablonFigury {       // tablica z kontrolą zakresu indeksów
    enum { n = 10000 }; // -> C++11 pozwala na: static  const n = 100;
    T* w[n];
    public:T& operator[](int i) { // operator indeksowania tablicy
        if(i < 0 || i >= (n-1)) {std::cerr<< "Indeks poza zakresem: " << i << std::endl;
            std::exit(1);
            
        }
        return *w[n-1];
        
    } // operator[]
}; // classWektor
#endif

figury.cpp

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#include <string>
#include <iostream>
#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt
#include "odcinek.h"  // Plik nagłówkowy dla klasy Figura
#include "trojkat.h"   // Plik nagłówkowy dla klasy Punkt
#include "prostokat.h"  // Plik nagłówkowy dla klasy Figura
#include "okrag.h"   // Plik nagłówkowy dla klasy Punkt
#include "szablon.h"


using namespace std;

using namespace MojeFigury;

int main() {
    try {
        // deklaracje, definicje, instrukcje, wywołania funkcji, ...
    } catch(Odcinek::ZleKonce& zk) {
        // obsługa wyjątku dla konstruktora odcinka, np. 
        zk.Komunikat();
    } catch(Trojkat::ZleWierzcholki& zw) {
        // obsługa wyjątku dla konstruktora trójkąta
        zw.Komunikat();
    } catch(Prostokat::ZleBoki& zb) {
        // obsługa wyjątku dla konstruktora prostokąta
        zb.Komunikat();
    } catch(Okrag::ZlyPromien& zp) {
        // obsługa wyjątku dla konstruktora okręgu
        zp.Komunikat();
    } catch(Figura::ZlaSkala& zs) {
        // obsługa wyjątku dla metody skaluj()
        zs.Komunikat();
    } catch(...) { // domyślna obsługa wyjątków
      cerr << "Nieznany wyjątek" << endl;
      exit(1);
    }
    Odcinek odc1(Punkt(0,1), Punkt(0,3));
    odc1.rysuj();
    odc1.przesun(2,5);
    odc1.skaluj(3);
    odc1.rysuj();
    Odcinek* odc2 = new Odcinek(Punkt(2,6), Punkt(8,2));
    odc2->rysuj();
    odc2->przesun(4,1);
    odc2->skaluj(2);
    odc2->rysuj();
    
    Trojkat troj1(Punkt(0,1), Punkt(0,3), Punkt(1,3));
    troj1.rysuj();
    troj1.przesun(4,1);
    troj1.skaluj(2);
    troj1.rysuj();
    Trojkat* troj2 = new Trojkat(Punkt(1,2), Punkt(4,1), Punkt(5,5));
    troj2->rysuj();
    troj2->przesun(4,1);
    troj2->rysuj();
    troj2->skaluj(2);
    troj2->rysuj();
     
    
    Prostokat pros1(Punkt(3,1), 3, 5);
    pros1.rysuj();
    pros1.przesun(7,3);
    pros1.skaluj(7);
    pros1.rysuj();
    Prostokat* pros2 = new Prostokat(Punkt(1,2), 5, 2);
    pros2->rysuj();
    pros2->przesun(5,6);
    pros2->skaluj(5);
    pros2->rysuj();    

    Okrag okr1(Punkt(4,3), 25);
    okr1.rysuj();
    okr1.przesun(11,7);
    okr1.rysuj();
    okr1.skaluj(4);  
    okr1.rysuj();
    Okrag* okr2 = new Okrag(Punkt(15,3), 5);
    okr2->rysuj();
    okr2->przesun(4,2);
    okr2->skaluj(14);  
    okr2->rysuj();

    cout << "\n\n\n\n" << endl;

    
    
    Figura* wd[10];


    wd[0] = &odc1;
    wd[1] = &troj1;
    wd[2] = &pros1;
    wd[3] = &okr1;
    wd[4] = odc2;
    wd[5] = troj2;
    wd[6] = pros2;
    wd[7] = okr2;

    for(int i = 0; i <= 7; i++ )
    {
        wd[i]->rysuj();
    }
    cout << "\n\n\n\n" << endl;
    
    
    SzablonFigury <Figura> wi;       
    for (int i = 0; i < 7; i++) {
        wi[i].rysuj();
    }


    delete odc2; odc2 = 0; 
    delete troj2; troj2 = 0;
    delete pros2; pros2 = 0;
    delete okr2; okr2 = 0;
    
    return 0;
    
}
    

1

Przy użyciu szablonów utworzyć kontener w postaci tablicy z kontrolą zakresów i rozmiarem przekazywanym przez argument szablonu (patrz wykład 17), następnie przy jego pomocy zdefiniować tablicę wskaźników dla klasy Figura i zademonstrować polimorfizm jak wyżej.

template <typename T> // definicja szablonu klasy dla typu T
class SzablonFigury {       // tablica z kontrolą zakresu indeksów
    enum { n = 10000 }; // -> C++11 pozwala na: static  const n = 100;
    T* w[n];

rozmiarem przekazywanym przez argument szablonu

Tak dla pewności:

rozmiarem przekazywanym przez argument szablonu

Chyba widzisz tu pewne braki w wykonaniu polecenia.

Dalej:

T* w[n];

Dlaczego robisz tablicę wskaźników?

utworzyć kontener w postaci tablicy z kontrolą zakresów i rozmiarem przekazywanym przez argument szablonu

To oznacza, że masz utworzyć generyczny kontener (taki jak std::array), a

następnie przy jego pomocy zdefiniować tablicę wskaźników dla klasy Figura

To również oznacza, że nazwa SzablonFigury na generyczną tablicę jest kompletnie nietrafiona i myląca.

0
kq napisał(a):

Przy użyciu szablonów utworzyć kontener w postaci tablicy z kontrolą zakresów i rozmiarem przekazywanym przez argument szablonu (patrz wykład 17), następnie przy jego pomocy zdefiniować tablicę wskaźników dla klasy Figura i zademonstrować polimorfizm jak wyżej.

template <typename T> // definicja szablonu klasy dla typu T
class SzablonFigury {       // tablica z kontrolą zakresu indeksów
    enum { n = 10000 }; // -> C++11 pozwala na: static  const n = 100;
    T* w[n];

rozmiarem przekazywanym przez argument szablonu

Tak dla pewności:

rozmiarem przekazywanym przez argument szablonu

Chyba widzisz tu pewne braki w wykonaniu polecenia.

Dalej:

T* w[n];

Dlaczego robisz tablicę wskaźników?

utworzyć kontener w postaci tablicy z kontrolą zakresów i rozmiarem przekazywanym przez argument szablonu

To oznacza, że masz utworzyć generyczny kontener (taki jak std::array), a

następnie przy jego pomocy zdefiniować tablicę wskaźników dla klasy Figura

To również oznacza, że nazwa SzablonFigury na generyczną tablicę jest kompletnie nietrafiona i myląca.

Zrobiłem to tak

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#ifndef FIGURY_H
#define FIGURY_H
#include <iostream>
#include <cstdlib>
#include <string>
#include <iostream>
#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt
#include "odcinek.h"  // Plik nagłówkowy dla klasy Figura
#include "trojkat.h"   // Plik nagłówkowy dla klasy Punkt
#include "prostokat.h"  // Plik nagłówkowy dla klasy Figura
#include "okrag.h"   // Plik nagłówkowy dla klasy Punkt

using namespace std;

using namespace MojeFigury;

template <typename T, int n = 100 >  // definicja szablonu klasy
class TabWskFig {       // tablica z kontrolą zakresu indeksów
    T tab[n];
    public:T& operator[](int i) { // operator indeksowania tablicy
        if(i < 0 || i >= n) {
            std::cerr<< "Indeks poza zakresem: " << i << std::endl;std::exit(1);
            }
            return tab[i];
    } // operator[]
    int rozmiar() const { return n; }
}; // classWektor
#endif

Zrobiłem funkcję w szablonie zwracającą rozmiar tablicy i tablicę w szablonie nie robiłem wskaźnikową tylko zwykłą

Teraz tak. W swoim programie. Część kodu, którą zmieniłem:

    TabWskFig* <int> ti;          // tablica dla into rozmiarze 100
    cout << "Rozmiar tablicy ti= " << ti->rozmiar() << endl;
    
    ti[0] = &odc1;
    ti[1] = &troj1;
    ti[2] = &pros1;
    ti[3] = &okr1;
    ti[4] = odc2;
    ti[5] = troj2;
    ti[6] = pros2;
    ti[7] = okr2;
    
    for (int i = 0; i < ti->rozmiar(); i++) {
        ti[i] ->rysuj();
    }

Czyli tak, zrobiłem teraz tablicę wskaźników i dałem do niej te obiekty/adresy obiektów. No i to przekazanie przez argument szablonu to dobrze zrobiłem, czy jakoś inaczej się to robi? Zmieniłem też nazwę.
Pomimo zmian nadal sporo błędów i program się zupełnie nie kompiluje, ciekawe dlaczego?

figury.cpp: In function ‘int main()’:
figury.cpp:110:14: error: missing template arguments before ‘*’ token
     TabWskFig* <int> ti;          // tablica dla into rozmiarze 100
              ^
figury.cpp:110:16: error: expected primary-expression before ‘<’ token
     TabWskFig* <int> ti;          // tablica dla into rozmiarze 100
                ^
figury.cpp:110:17: error: expected primary-expression before ‘int’
     TabWskFig* <int> ti;          // tablica dla into rozmiarze 100
                 ^~~
figury.cpp:111:39: error: ‘ti’ was not declared in this scope
     cout << "Rozmiar tablicy ti= " << ti->rozmiar() << endl;
                                       ^~
figury.cpp:111:39: note: suggested alternative: ‘tm’
     cout << "Rozmiar tablicy ti= " << ti->rozmiar() << endl;
                                       ^~
                                       tm

Cały kod w tym pliku figury.cpp

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#include <string>
#include <iostream>
#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt
#include "odcinek.h"  // Plik nagłówkowy dla klasy Figura
#include "trojkat.h"   // Plik nagłówkowy dla klasy Punkt
#include "prostokat.h"  // Plik nagłówkowy dla klasy Figura
#include "okrag.h"   // Plik nagłówkowy dla klasy Punkt
#include "szablon.h"


using namespace std;

using namespace MojeFigury;

int main() {
    try {
        // deklaracje, definicje, instrukcje, wywołania funkcji, ...
    } catch(Odcinek::ZleKonce& zk) {
        // obsługa wyjątku dla konstruktora odcinka, np. 
        zk.Komunikat();
    } catch(Trojkat::ZleWierzcholki& zw) {
        // obsługa wyjątku dla konstruktora trójkąta
        zw.Komunikat();
    } catch(Prostokat::ZleBoki& zb) {
        // obsługa wyjątku dla konstruktora prostokąta
        zb.Komunikat();
    } catch(Okrag::ZlyPromien& zp) {
        // obsługa wyjątku dla konstruktora okręgu
        zp.Komunikat();
    } catch(Figura::ZlaSkala& zs) {
        // obsługa wyjątku dla metody skaluj()
        zs.Komunikat();
    } catch(...) { // domyślna obsługa wyjątków
      cerr << "Nieznany wyjątek" << endl;
      exit(1);
    }
    Odcinek odc1(Punkt(0,1), Punkt(0,3));
    odc1.rysuj();
    odc1.przesun(2,5);
    odc1.skaluj(3);
    odc1.rysuj();
    Odcinek* odc2 = new Odcinek(Punkt(2,6), Punkt(8,2));
    odc2->rysuj();
    odc2->przesun(4,1);
    odc2->skaluj(2);
    odc2->rysuj();
    
    Trojkat troj1(Punkt(0,1), Punkt(0,3), Punkt(1,3));
    troj1.rysuj();
    troj1.przesun(4,1);
    troj1.skaluj(2);
    troj1.rysuj();
    Trojkat* troj2 = new Trojkat(Punkt(1,2), Punkt(4,1), Punkt(5,5));
    troj2->rysuj();
    troj2->przesun(4,1);
    troj2->rysuj();
    troj2->skaluj(2);
    troj2->rysuj();
     
    
    Prostokat pros1(Punkt(3,1), 3, 5);
    pros1.rysuj();
    pros1.przesun(7,3);
    pros1.skaluj(7);
    pros1.rysuj();
    Prostokat* pros2 = new Prostokat(Punkt(1,2), 5, 2);
    pros2->rysuj();
    pros2->przesun(5,6);
    pros2->skaluj(5);
    pros2->rysuj();    

    Okrag okr1(Punkt(4,3), 25);
    okr1.rysuj();
    okr1.przesun(11,7);
    okr1.rysuj();
    okr1.skaluj(4);  
    okr1.rysuj();
    Okrag* okr2 = new Okrag(Punkt(15,3), 5);
    okr2->rysuj();
    okr2->przesun(4,2);
    okr2->skaluj(14);  
    okr2->rysuj();

    cout << "\n\n\n\n" << endl;

    
    
    Figura* wd[10];


    wd[0] = &odc1;
    wd[1] = &troj1;
    wd[2] = &pros1;
    wd[3] = &okr1;
    wd[4] = odc2;
    wd[5] = troj2;
    wd[6] = pros2;
    wd[7] = okr2;

    for(int i = 0; i <= 7; i++ )
    {
        wd[i]->rysuj();
    }
    cout << "\n\n\n\n" << endl;

    
    TabWskFig* <int> ti;          // tablica dla into rozmiarze 100
    cout << "Rozmiar tablicy ti= " << ti->rozmiar() << endl;
    
    ti[0] = &odc1;
    ti[1] = &troj1;
    ti[2] = &pros1;
    ti[3] = &okr1;
    ti[4] = odc2;
    ti[5] = troj2;
    ti[6] = pros2;
    ti[7] = okr2;
    
    for (int i = 0; i < ti->rozmiar(); i++) {
        ti[i] ->rysuj();
    }

    delete odc2; odc2 = 0; 
    delete troj2; troj2 = 0;
    delete pros2; pros2 = 0;
    delete okr2; okr2 = 0;
    
    return 0;
    
}
1
TabWskFig* <int> ti;

Masz mieć tablicę, a nie wskaźnik na nią. Dalej, ma to być tablica wskaźników na figurę, a nie intów. Jeszcze dalej: zadanie wyraźnie mówi, że szablon tablicy ma być ogólny, nie ma tam ani słowa o tym, że ma być w jakikolwiek sposób specjalnie przystosowany do trzymania wskaźników lub figur.

Może to Ci pomoże: usuń to co masz (albo zakomentuj) i wykonaj drugą część zadania, za szablon tablicy wykorzystując std::array. Jak zobaczysz jak tego powinieneś użyć, to i zrozumienie jak to zaimplementować będzie łatwiejsze.

Dla pewności: napisz to w taki sposób, aby można było podmienić std::array na Twoją klasę i aby to była jedyna zmiana, której będzie wymagał kod.

0

Zrobiłem to tak i nie mam żadnych błędów w kompilacji

    Tablica <Figura*> ti;          // tablica dla into rozmiarze 100
    cout << "Rozmiar tablicy ti= " << ti.rozmiar() << endl;
    
    ti[0] = &odc1;
    ti[1] = &troj1;
    ti[2] = &pros1;
    ti[3] = &okr1;
    ti[4] = odc2;
    ti[5] = troj2;
    ti[6] = pros2;
    ti[7] = okr2;
    
    for (int i = 0; i < ti.rozmiar(); i++) {
        ti[i] ->rysuj();
    }

Ale wciąż błąd segmentacji.

Ma to być ogólne, jeśli chodzi o nazwę to dałem po prostu Tablica. Dałem też, żeby wskazywał na figurę, bo miałeś rację, że wskazuje na int

Strasznie to trudne. Mieliśmy kurs z C przez 3 miesiące i w ostatnie cztery tygodnie nam wrzucili całego C++ i nie zdążyłem uczyć się z książki, bo potem poczytam tego Bjarne Stroutsopa, który napisał o tym książkę, ale ona strasznie duża jest podobno. Na razie uczyłem się jedynie z wykładów, które są chude i kilka filmików z PasjaInformatyki na youtube, ale niektó©zy tutaj się krytycznie wypowiadali o tym panu z tamtych filmów, więc poczytam książkę.

Tutaj cały kod figury.cpp

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#include <string>
#include <iostream>
#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt
#include "odcinek.h"  // Plik nagłówkowy dla klasy Figura
#include "trojkat.h"   // Plik nagłówkowy dla klasy Punkt
#include "prostokat.h"  // Plik nagłówkowy dla klasy Figura
#include "okrag.h"   // Plik nagłówkowy dla klasy Punkt
#include "szablon.h"


using namespace std;

using namespace MojeFigury;

int main() {
    try {
        // deklaracje, definicje, instrukcje, wywołania funkcji, ...
    } catch(Odcinek::ZleKonce& zk) {
        // obsługa wyjątku dla konstruktora odcinka, np. 
        zk.Komunikat();
    } catch(Trojkat::ZleWierzcholki& zw) {
        // obsługa wyjątku dla konstruktora trójkąta
        zw.Komunikat();
    } catch(Prostokat::ZleBoki& zb) {
        // obsługa wyjątku dla konstruktora prostokąta
        zb.Komunikat();
    } catch(Okrag::ZlyPromien& zp) {
        // obsługa wyjątku dla konstruktora okręgu
        zp.Komunikat();
    } catch(Figura::ZlaSkala& zs) {
        // obsługa wyjątku dla metody skaluj()
        zs.Komunikat();
    } catch(...) { // domyślna obsługa wyjątków
      cerr << "Nieznany wyjątek" << endl;
      exit(1);
    }
    Odcinek odc1(Punkt(0,1), Punkt(0,3));
    odc1.rysuj();
    odc1.przesun(2,5);
    odc1.skaluj(3);
    odc1.rysuj();
    Odcinek* odc2 = new Odcinek(Punkt(2,6), Punkt(8,2));
    odc2->rysuj();
    odc2->przesun(4,1);
    odc2->skaluj(2);
    odc2->rysuj();
    
    Trojkat troj1(Punkt(0,1), Punkt(0,3), Punkt(1,3));
    troj1.rysuj();
    troj1.przesun(4,1);
    troj1.skaluj(2);
    troj1.rysuj();
    Trojkat* troj2 = new Trojkat(Punkt(1,2), Punkt(4,1), Punkt(5,5));
    troj2->rysuj();
    troj2->przesun(4,1);
    troj2->rysuj();
    troj2->skaluj(2);
    troj2->rysuj();
     
    
    Prostokat pros1(Punkt(3,1), 3, 5);
    pros1.rysuj();
    pros1.przesun(7,3);
    pros1.skaluj(7);
    pros1.rysuj();
    Prostokat* pros2 = new Prostokat(Punkt(1,2), 5, 2);
    pros2->rysuj();
    pros2->przesun(5,6);
    pros2->skaluj(5);
    pros2->rysuj();    

    Okrag okr1(Punkt(4,3), 25);
    okr1.rysuj();
    okr1.przesun(11,7);
    okr1.rysuj();
    okr1.skaluj(4);  
    okr1.rysuj();
    Okrag* okr2 = new Okrag(Punkt(15,3), 5);
    okr2->rysuj();
    okr2->przesun(4,2);
    okr2->skaluj(14);  
    okr2->rysuj();

    cout << "\n\n\n\n" << endl;

    
    
    Figura* wd[10];


    wd[0] = &odc1;
    wd[1] = &troj1;
    wd[2] = &pros1;
    wd[3] = &okr1;
    wd[4] = odc2;
    wd[5] = troj2;
    wd[6] = pros2;
    wd[7] = okr2;

    for(int i = 0; i <= 7; i++ )
    {
        wd[i]->rysuj();
    }
    cout << "\n\n\n\n" << endl;

    
    Tablica <Figura*> ti;          // tablica dla into rozmiarze 100
    cout << "Rozmiar tablicy ti= " << ti.rozmiar() << endl;
    
    ti[0] = &odc1;
    ti[1] = &troj1;
    ti[2] = &pros1;
    ti[3] = &okr1;
    ti[4] = odc2;
    ti[5] = troj2;
    ti[6] = pros2;
    ti[7] = okr2;
    
    for (int i = 0; i < ti.rozmiar(); i++) {
        ti[i] ->rysuj();
    }

    delete odc2; odc2 = 0; 
    delete troj2; troj2 = 0;
    delete pros2; pros2 = 0;
    delete okr2; okr2 = 0;
    
    return 0;
    
}
    

szablon.h

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#ifndef FIGURY_H
#define FIGURY_H
#include <iostream>
#include <cstdlib>
#include <string>
#include <iostream>
#include "figura.h"  // Plik nagłówkowy dla klasy Figura
#include "punkt.h"   // Plik nagłówkowy dla klasy Punkt
#include "odcinek.h"  // Plik nagłówkowy dla klasy Figura
#include "trojkat.h"   // Plik nagłówkowy dla klasy Punkt
#include "prostokat.h"  // Plik nagłówkowy dla klasy Figura
#include "okrag.h"   // Plik nagłówkowy dla klasy Punkt

using namespace std;

using namespace MojeFigury;

template <typename T, int n = 100 >  // definicja szablonu klasy
class Tablica {       // tablica z kontrolą zakresu indeksów
    T tab[n];
    public:T& operator[](int i) { // operator indeksowania tablicy
        if(i < 0 || i >= n) {
            std::cerr<< "Indeks poza zakresem: " << i << std::endl;std::exit(1);
            }
            return tab[i];
    } // operator[]
    int rozmiar() const { return n; }
}; // classWektor
#endif
2

Teraz to chyba spełnia warunki zadania. Osobiście wolałbym angielskie nazwy i możliwość trywialnej podmiany na std::array, ale najważniejsze zostało zrobione.

Błąd segmentacji zapewne dlatego:

    for (int i = 0; i < ti.rozmiar(); i++) {
        ti[i] ->rysuj();
    }

Rozmiar to 100, bo używasz domyślnego, a uzupełniasz tylko 8. Debugger by Ci to powiedział :​)

0

Mój program został oceniony, dostałem taką odpowiedź:
Dostałem 4,5 za co jestem wdzięczny wam wszystkim za rady. Jednak było pewne zastrzeżenie

Wszystkie implementacje wyglądają dobrze, ale zastosowanie bloku try .. catch jest złe.

Blok try musi obejmować testowane polecenia. W tym projekcie blok try jest pusty, a wszystkie polecenia za nim nie będą przechwytywane. Stad program zostanie przerwany poprzez komunikat o nieprzechwyceniu wyjątku.

Czyli co powinenem tu zrobić np.

int main() {
    try {
        // deklaracje, definicje, instrukcje, wywołania funkcji, ...
    } catch(Odcinek::ZleKonce& zk) {
        // obsługa wyjątku dla konstruktora odcinka, np. 
        zk.Komunikat();
    } catch(Trojkat::ZleWierzcholki& zw) {
        // obsługa wyjątku dla konstruktora trójkąta
        zw.Komunikat();
    } catch(Prostokat::ZleBoki& zb) {
        // obsługa wyjątku dla konstruktora prostokąta
        zb.Komunikat();
    } catch(Okrag::ZlyPromien& zp) {
        // obsługa wyjątku dla konstruktora okręgu
        zp.Komunikat();
    } catch(Figura::ZlaSkala& zs) {
        // obsługa wyjątku dla metody skaluj()
        zs.Komunikat();
    } catch(...) { // domyślna obsługa wyjątków
      cerr << "Nieznany wyjątek" << endl;
      exit(1);
    }

Przecież wyświetlam odpowiednie komunikaty, co oprócz tego trzeba zrobić?

0

Jeśli spojrzysz na swój kod, to faktycznie wszystkie polecenia są poza blokiem try/catch. Jeśli chcesz go użyć to użyj go poprawnie.

0
kq napisał(a):

Jeśli spojrzysz na swój kod, to faktycznie wszystkie polecenia są poza blokiem try/catch. Jeśli chcesz go użyć to użyj go poprawnie.

Aha czyli całość wywołań funkcji w main wsadzam do tego bloku, tak jak teraz.
Zrobiłem to tak

// Plikfigura.h
// Klasa abstrakcyjna Figura dla figur geometrycznych
#include <string>
#include <iostream>
#include "program_figura.h"  // Plik nagłówkowy dla klasy Figura
#include "program_punkt.h"   // Plik nagłówkowy dla klasy Punkt
#include "program_odcinek.h"  // Plik nagłówkowy dla klasy Figura
#include "program_trojkat.h"   // Plik nagłówkowy dla klasy Punkt
#include "program_prostokat.h"  // Plik nagłówkowy dla klasy Figura
#include "program_okrag.h"   // Plik nagłówkowy dla klasy Punkt
#include "program_szablon.h"


using namespace std;

using namespace MojeFigury;

int main() {
    try {
        // deklaracje, definicje, instrukcje, wywołania funkcji, ...
    Odcinek odc1(Punkt(0,1), Punkt(0,3));
    odc1.rysuj();
    odc1.przesun(2,5);
    odc1.skaluj(3);
    odc1.rysuj();
    Odcinek* odc2 = new Odcinek(Punkt(2,6), Punkt(8,2));
    odc2->rysuj();
    odc2->przesun(4,1);
    odc2->skaluj(2);
    odc2->rysuj();
    
    Trojkat troj1(Punkt(0,1), Punkt(0,3), Punkt(1,3));
    troj1.rysuj();
    troj1.przesun(4,1);
    troj1.skaluj(2);
    troj1.rysuj();
    Trojkat* troj2 = new Trojkat(Punkt(1,2), Punkt(4,1), Punkt(5,5));
    troj2->rysuj();
    troj2->przesun(4,1);
    troj2->rysuj();
    troj2->skaluj(2);
    troj2->rysuj();
     
    
    Prostokat pros1(Punkt(3,1), 3, 5);
    pros1.rysuj();
    pros1.przesun(7,3);
    pros1.skaluj(7);
    pros1.rysuj();
    Prostokat* pros2 = new Prostokat(Punkt(1,2), 5, 2);
    pros2->rysuj();
    pros2->przesun(5,6);
    pros2->skaluj(5);
    pros2->rysuj();    

    Okrag okr1(Punkt(4,3), 25);
    okr1.rysuj();
    okr1.przesun(11,7);
    okr1.rysuj();
    okr1.skaluj(4);  
    okr1.rysuj();
    Okrag* okr2 = new Okrag(Punkt(15,3), 5);
    okr2->rysuj();
    okr2->przesun(4,2);
    okr2->skaluj(14);  
    okr2->rysuj();

    cout << "\n\n\n\n" << endl;
    
    Figura* wd[10];


    wd[0] = &odc1;
    wd[1] = &troj1;
    wd[2] = &pros1;
    wd[3] = &okr1;
    wd[4] = odc2;
    wd[5] = troj2;
    wd[6] = pros2;
    wd[7] = okr2;

    for(int i = 0; i <= 7; i++ )
    {
        wd[i]->rysuj();
    }
    cout << "\n\n\n\n" << endl;
    
    Tablica <Figura*> ti;          // tablica dla into rozmiarze 100
    cout << "Rozmiar tablicy ti= " << ti.rozmiar() << endl;
    
    ti[0] = &odc1;
    ti[1] = &troj1;
    ti[2] = &pros1;
    ti[3] = &okr1;
    ti[4] = odc2;
    ti[5] = troj2;
    ti[6] = pros2;
    ti[7] = okr2;
   
    for (int i = 0; i < ti.rozmiar(); i++) {
        ti[i] ->rysuj();
    }
    delete odc2; odc2 = 0; 
    delete troj2; troj2 = 0;
    delete pros2; pros2 = 0;
    delete okr2; okr2 = 0;
    } catch(Odcinek::ZleKonce& zk) {
        // obsługa wyjątku dla konstruktora odcinka, np. 
        zk.Komunikat();
    } catch(Trojkat::ZleWierzcholki& zw) {
        // obsługa wyjątku dla konstruktora trójkąta
        zw.Komunikat();
    } catch(Prostokat::ZleBoki& zb) {
        // obsługa wyjątku dla konstruktora prostokąta
        zb.Komunikat();
    } catch(Okrag::ZlyPromien& zp) {
        // obsługa wyjątku dla konstruktora okręgu
        zp.Komunikat();
    } catch(Figura::ZlaSkala& zs) {
        // obsługa wyjątku dla metody skaluj()
        zs.Komunikat();
    } catch(...) { // domyślna obsługa wyjątków
      cerr << "Nieznany wyjątek" << endl;
      exit(1);
    }
    return 0;   
}
0

Masz niepoprawny destruktor dla figury.
Zaś komentarz przy nim ... to jakaś stenografia delirium?
Może to pomoże zrozumieć problem:

#include <iostream>
using namespace std;

class YourBase
  {
   public:
   YourBase() { cout<<"Constructor YourBase"<<endl; }
   ~YourBase() { cout<<"Destructor YourBase"<<endl; }
  };
  
class YourDerived:public YourBase
  {
   public:
   YourDerived() { cout<<"Constructor YourDerived"<<endl; }
   ~YourDerived() { cout<<"Destructor YourDerived"<<endl; }
  };
  
class SameBase
  {
   public:
   SameBase() { cout<<"Constructor SameBase"<<endl; }
   // brak destruktora, ten sam problem
  };
  
class SameDerived:public SameBase
  {
   public:
   SameDerived() { cout<<"Constructor SameDerived"<<endl; }
   ~SameDerived() { cout<<"Destructor SameDerived"<<endl; } 
  };
  
class GoodBase
  {
   public:
   GoodBase() { cout<<"Constructor GoodBase"<<endl; }
   virtual ~GoodBase() { cout<<"Destructor GoodBase"<<endl; }
  };
  
class GoodDerived:public GoodBase
  {
   public:
   GoodDerived() { cout<<"Constructor GoodDerived"<<endl; }
   ~GoodDerived() { cout<<"Destructor GoodDerived"<<endl; }
  };
  
int main()
  {
   GoodBase *gb=new GoodDerived(); delete gb; // wywołano destruktor GoodDerived
   YourBase *yb=new YourDerived(); delete yb; // nie wywołano destruktora YourDerived
   cout<<"A gdzie wywolanie destruktora YourDerived?"<<endl;
   SameBase *sb=new SameDerived(); delete sb; // nie wywołano destruktora YourDerived
   cout<<"A gdzie wywolanie destruktora SameDerived?"<<endl;
   return 0;
  }

Podsumowując:
Klasa bazowa musi posiadać wirtualny destruktor, na wypadek gdyby klasa pochodna musiała mieć destruktor, zaś obiekty pochodne przechowywane w sposób polimorficzny, czyli jako wskaźnik/referencja na klasę bazową.

0

Klasa wcale nie musi mieć **wirtualnego **destruktora, aby obiekty klasy pochodne były poprawnie usuwane. Wystarczy użyć shared_ptr<>

#include <iostream>
#include <memory>
#include <vector>

using namespace std;

class YourBase
{
public:
    YourBase() { cout << "Constructor YourBase"<< endl; }
   ~YourBase() { cout << "Destructor YourBase" << endl; }
};

class YourDerived1 : public YourBase
{
public:
    YourDerived1() { cout << "Constructor YourDerived1" << endl; }
   ~YourDerived1() { cout << "Destructor YourDerived1"  << endl; }
};

class YourDerived2 : public YourBase
{
public:
    YourDerived2() { cout << "Constructor YourDerived2" << endl; }
   ~YourDerived2() { cout << "Destructor YourDerived2" << endl; }
};


int main()
{
    vector<shared_ptr<YourBase>> pool(5);

    for( size_t index {0} ; index<pool.size() ; ++index )
    {
        index%2 == 0 ? pool[index] = make_shared<YourDerived1>() : pool[index] = make_shared<YourDerived2>();
    }

    return 0;
}

Takie rozwiązanie może być użyteczne, kiedy chcemy stworzyć własną klasę dziedziczącą po klasie kontenera STLu ( np. vector ), który jak wiadomo nie posiada wirtualnego destruktora.

3

Bardzo niebezpieczna rada.
Nie masz gwarancji, że ktoś będzie używał shared_ptr do danej klasy.
Wystarczy unique_ptr i już jest kwiatek: https://godbolt.org/z/3vM5G5

0

Nie masz gwarancji, że ktoś będzie używał shared_ptr do danej klasy

#include <iostream>
#include <memory>
#include <vector>

using namespace std;

class Base
{
public:
    Base() { cout << "Constructor Base" << endl; }
   ~Base() { cout << "Destructor Base"  << endl; }
};

class Derived : public Base
{
public:
    static auto create()
    {
        return shared_ptr<Derived>(new Derived());
    }
    ~Derived() { cout << "Destructor Derived" << endl; }
private:
    Derived() { cout << "Constructor Derived"  << endl; };
};

int main()
{
    shared_ptr<Base> good_object { Derived::create() };

    // not compile
    // unique_ptr<Base> bad_object1 { Derived::create() };
    // Base* bad_object2 { new Derived() };
    // unique_ptr<Base> bad_object3 = make_unique<Derived>();

    return 0;
}

Jednakże IMHO używanie shared_pt w taki sposób powinno być ograniczone do sytuacji wyjątkowych, w których na przykład musimy dziedziczyć po klasach z niemodyfikowalnej biblioteki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1