1 Востаннє редагувалося shtyegor (25.01.2017 18:22:45)

Тема: допоможіть початківцю

ннн

2

Re: допоможіть початківцю

Заголовок беззмістовний.
Помилку ми не допоможемо виправити, оскільки ви не написали, що ви хочете зробити і що у вас за помилка. Наприклад, компілятор може повідомляти, що у вас закінчилося місце на диску - як ми це побачимо? А якщо ми навіть здогадаємося, що у вас за помилка - як ми напишемо, що треба, якщо ви не сказали, що має робити програма?
Матеріалів про вказівники (чи показчики, але не поїнтери і не указатєлі) купа. Що ви вже прочитали?
Ну і дуже не раджу лізти в ООП до того, як розберетеся із вказівниками.

3 Востаннє редагувалося shtyegor (25.01.2017 18:15:27)

Re: допоможіть початківцю

Можете оцінити код і сказати в чому заключаєтьсяся моя криворукість, і як робити правельніше (помилку я знайшов)

#include <iostream>

using namespace::std;

class Address{
    private:
        int index;
        char *country;
        char *city;
        char *street;
        int house;
    public:
        Address(){}
        Address(int initialIndex, char *initialCountry, char *initialCity, char *initialStreet, int initialHouse){
            index = initialIndex;
            country = initialCountry;
            city = initialCity;
            street = initialStreet;
            house = initialHouse;
        }
        ~Address(){
            delete [] country;
            delete [] city;
            delete [] street;
        }
        int getIndex(){
            return index;
        }
        char* getCountry(){
            return country;
        }
        char* getCity(){
            return city;
        }
        char* getStreet(){
            return street;
        }
        int getHouse(){
            return house;
        }
        
        void printAddress(){
            cout << getIndex() << " " << getCountry() << " " << getCity() << " " << getStreet() << " " << getHouse() << "\n";      
        }
        
        void setIndex(int newIndex){
            index = newIndex;
        }
        void setCountry(char *newCountry){
            country = newCountry;
        }
        void setCity(char *newCity){
            city = newCity;
        }
        void setStreet(char *newStreet){
            street = newStreet;
        }
        void setHouse(int newHouse){
            house = newHouse;
        }
        void setAddress(int newIndex, char *newCountry, char *newCity, char *newStreet, int newHouse){
            index = newIndex;
            country = newCountry;
            city = newCity;
            street = newStreet;
            house = newHouse;
        }
        
};

int main(){
    Address *address = new Address[2];
    address[0].setAddress(4300, "Ukraine", "Kiev", "Kirovskaya", 99);
    address[1].setAddress(7400, "Ukraine", "Kiev", "Mitnutska", 12);
    
    address[0].printAddress();
    address[1].printAddress();
    
}

4

Re: допоможіть початківцю

Якщо вам передають сирий вказівник, то вам не варто його видаляти delete. Якщо б ви його скопіювали в член класу якось за допомогою new і strcpy, то потім видаляти нормально. Також може мати сенс використати std::string.

Подякували: shtyegor, varkon2

5

Re: допоможіть початківцю

Якщо деталізувати попередню відповідь - основна помилка у вас у тому, що ваші поля класу вказують на пам'ять яку ви не контролюєте. А ви навіть не перевіряєте їх валідність. Тож наприклад цілком валідний код

//far far away
std::string street, country, city;
...
//ups!
address[1].setAddress(7400, country.c_str(), city.c_str(),street.c_str(), 12);

на пустому місці приведе до проблем.
Тому (якщо у вас немає нууууудуже поважної причини цього не робити)   слід дотримуватися:
1) Завжди використовуйте std::string
2) Якщо не маєте інших причин - передавайте за просилянням а не за вказівником
3) Якщо використовуєте вказівник - необхідно перевіряти його валідність.
P.S. Для індексу та номера будинку (а також квартир, блоків та ін.) використовують строки.

Подякували: 0xDADA11C7, Yola2

6

Re: допоможіть початківцю

Вам необхідно розібратися що таке С а що таке С++.

#include <iostream>
using namespace::std;
cout << getIndex() << " " << getCountry() << " " << getCity() << " " << getStreet() << " " << getHouse() << "\n";      

Це С++, все інше у вас на С
(У С++ не рекомендується використовувати вказівники та вручну керувати пам'яттю)

Якщо доповнити попередні відповіді, то
Баг #1

"Kyiv" а не "Kiev"

Менш важлива проблема:

    address[0].setAddress(4300, "Ukraine", "Kiev", "Kirovskaya", 99);    {0}
                                                                         {1}
    address[0].printAddress();                                           {2}

В точці {0} ви створили тимчасові змінні, які передаються у функцію setAddress як rvalue.
Проблема rvalue в тому, що як тільки ви вийшли з функції setAddress (точка {1}), вони одразу ж видаляються з памяті!
Тому на момент виклику функції printAddress {2}, ваші вказівники вказуть на мусор!

Як правильно написати залежить на чому це треба зробити, на С чи С++

7

Re: допоможіть початківцю

@hesting, взагалі-то рядкові літерали це lvalue.

8

Re: допоможіть початківцю

lvalue це те що має ім'я. А маючи ім'я, йому можна змінювати значення.

9 Востаннє редагувалося ntkrnlpa.exe (27.01.2017 23:00:27)

Re: допоможіть початківцю

hesting написав:

lvalue це те що має ім'я. А маючи ім'я, йому можна змінювати значення.

ви явно перечитались вумних книжок на тему "шо нівякомуразі(R) ніззя робити в с++". того ви знаєте такі вумні слова як lvalue і rvalue, а от по суті нопейсали фігню вибачте на слові. :D
ось тут нопейсале:

В точці {0} ви створили тимчасові змінні, які передаються у функцію setAddress як rvalue.
Проблема rvalue в тому, що як тільки ви вийшли з функції setAddress (точка {1}), вони одразу ж видаляються з памяті!
Тому на момент виклику функції printAddress {2}, ваші вказівники вказуть на мусор!

Як правильно написати залежить на чому це треба зробити, на С чи С++

От подумайте, шо саме там "одразу видаляється з пам'яти"?
я як безнадійний сішник який ніасіліл с++ розумію, шо то вказівники на рядки. самі рядки - то глобальні безіменні константи, які лежатимуть спокійно в якій небудь RO секції, і звичайно, нікуди не будуть видалені. А вказівники на них які насправді передаються тій функції - то її аргументи, локальні для неї змінні власне, і вона копіює їхні значення в приватні для класу поля, і нічого не губиться, коли функція вертається і її стек зчищається разом з цими аргументами.

Подякували: 0x9111A, varkon2

10

Re: допоможіть початківцю

то глобальні безіменні константи, які лежатимуть спокійно в якій небудь RO секції, і звичайно, нікуди не будуть видалені.

дякую, поржав

11

Re: допоможіть початківцю

ви б краще відповіли шо ж там видаляється.

12

Re: допоможіть початківцю

Я уже все пояснив. Можу хіба що спростити код, щоб було легше зрозуміти
Як на вашу думку, що буде виведено?
Сподіваюсь на 2 відповіді, спочатку без компіляції, а потім після компіляції

#include <iostream>

const int& max(const int& a, const int& b)
{
    return (a > b) ? a : b;    
}

int main()
{
   const int lvalue1 = 100;
   const int lvalue2 = 50;
   
   const int& max1 = max(lvalue1, lvalue2);
   const int& max2 = max(lvalue1, 30);
   const int& max3 = max(lvalue1, 130);
   const int& max4 = max(200, 230);

   std::cout << max1 << '\n';
   std::cout << max2 << '\n';
   std::cout << max3 << '\n';
   std::cout << max4 << '\n';
   
   return 0;
}

13

Re: допоможіть початківцю

§ 5.1.1 написав:

Рядковий літерал це lvalue; всі інші літерали це prvalues.

§ 2.13.5 написав:

Наслідок спроби зміни рядкового літералу невизначений.

Ніхто не може видалити рядковий літерал. Хоча такий літерал може лежати і не в RO секції, але зазвичай таки там.

14

Re: допоможіть початківцю

Yola, модифікував задачу для вас:

#include <iostream>

const std::string& f(const std::string& a)
{
    return a;    
}

int main()
{
   const std::string lvalue = "lvalue";

   const std::string& f_lvalue = f(lvalue);
   const std::string& f_rvalue = f("rvalue");

   std::cout << "lvalue: " << f_lvalue << '\n';
   std::cout << "rvalue: " << f_rvalue << '\n';

   return 0;
}

15 Востаннє редагувалося Yola (28.01.2017 09:29:48)

Re: допоможіть початківцю

Отут ви можете створити тимчасовий об'єкт типу string, якщо ви його створюєте, то ви повертаєте посилання на локальну змінну:

const std::string& f(const std::string& a)

Тут нормально, ви передаєте string і відповідно тимчасова змінна не створюється:

const std::string lvalue = "lvalue";
const std::string& f_lvalue = f(lvalue);

А тут ви створюєте тимчасовий об'єкт:

const std::string& f_rvalue = f("rvalue");

А "rvalue" - це lvalue, стандарт каже.

16

Re: допоможіть початківцю

output яким буде?

17 Востаннє редагувалося Yola (28.01.2017 09:40:51)

Re: допоможіть початківцю

hesting написав:

output яким буде?

У мене вийшло ось, що:

консоль написав:

lvalue: lvalue
rvalue:

Але, гадаю, що це

const std::string& f(const std::string& a) { return a; }

невизначена поведінка, тому покладатись на такий вивід не можна.

Щоб уникнути невизначеної поведінки треба писати так (без посилання на локальну змінну):

const std::string f(const std::string& a) { return a; }

18

Re: допоможіть початківцю

У мене вийшло ось, що:
lvalue: lvalue
rvalue:

чому rvalue не роздрукувався? він жеж лежить в глобальній секції для тимчасових змінних!! значить ініціалізується на старті програми та має жити до її завершення!!


без посилання на локальну змінну

а не локальна зміна функції f(), це тимчасова зміна функції main(), нарізана на стеці, i як тільки завершився виклик f(), вона видалилась. Жодної глобальної секції для тимчасових обєктів не існує!

19

Re: допоможіть початківцю

Пане hesting, ви неуважно читали мої попередні повідомлення. Гляньте №15. У випадку коли ви передаєте const char* то створюється тимчасова змінна і ви повертаєте посилання на неї. А ця змінне перестає бути валідною там де закінчується вираз:

const std::string& f_rvalue = f("rvalue");

Тобто далі посилання на цю тимчасову змінну - невизначена поведінка.

20

Re: допоможіть початківцю

повідомлення #15 не зовсім чітко формулює вашу позицію

Yola написав:

А тут ви створюєте тимчасовий об'єкт:

const std::string& f_rvalue = f("rvalue");

А "rvalue" - це lvalue, стандарт каже.

поясніть детальніше, чому "rvalue" це по вашому lvalue ?