81

Re: Як створити екземпляр класу і як додати в нього конструктора?

FakiNyan
Молодець! Виходить зовсім непогано :)

Ще можна реалізувати:

  • копіюючий конструктор - MyString( cons MyString& )

  • оператор присвоєння - operator=

  • оператор контактенації - operator+

  • і звичайно ж функцію заміни символів - replace

Подякували: FakiNyan1

82

Re: Як створити екземпляр класу і як додати в нього конструктора?

Уточню: якщо ви в C++98 визначаєте конструктор копіювання, то маєте також визначити оператор присвоєння і деструктор (це зветься "правилом трійки"). Якщо ж це C++11/C++14, то вже діє "правило п'ятірки" - додаються конструктор переміщення і оператор присвоєння переміщення (const MyString&&).

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

83 Востаннє редагувалося FakiNyan (27.09.2014 21:16:47)

Re: Як створити екземпляр класу і як додати в нього конструктора?

Чуєте, я, взагалі, дуже боюся цієї теми, ну конструктора копіювання, тому що не зовсім розумію, що це, і навіщо воно.  Саме тому я досі не почав нічого писати на цю тему.

Прихований текст

Але я впевнений, що з вашою допомогою мені все стане зрозуміло, і я переможу конструктор копіювання!
Тому що ніхто і ніщо не запихне мені розуміння прямо в мозок, якщо я сам не буду того бажати, якщо я не відкрию свій мозок для нових знань! ліл, і тут в мене потекла сльоза

Ось цей код виводить дві однакові стрічки.

MyString mys = "abcdabe";
    std::cout << mys << std::endl;
    
    MyString mys1 = mys;

    std::cout << mys1 << std::endl;

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

MyString mys1 = mys;

Можливо, тоді стане зрозуміло, чому, коли я намагаюсь закрити консольку, в мене вилазить ось це

Прихований текст

http://не-дійсний-домен/bQ8X1/46d073e3dc.png

взагалі я вже бачив щось подібне, тому можу припустити, що йде намагання звільнити вже звільнену пам'ять.

UPD: я не втримався, і перевірив, змінив деструктор

    MyString::~MyString()
    {
        std::cout << "deleting at: "<< str << std::endl;
        delete[] str; //очищуємо місце, котре виділялось під оті ваші стрічки
    }

І от, що ми бачимо

Прихований текст

http://не-дійсний-домен/bQ995/5e300118bc.png

UPD2: Я трохи не так змінив деструктор, адже хтів перевірити, чи змінна str обох класів має однакову адресу, але ні, адреси різні.

Прихований текст

http://не-дійсний-домен/bQ9dq/2c20fc4cf0.png

Тоді, що ж коється при

MyString mys1 = mys;

???
Подивився інфу в інетах, ще більше заплутався, купа якихось конструкторів, якісь ще &&, а що, а навіщо, просто срака якась.

84 Востаннє редагувалося koala (27.09.2014 21:46:53)

Re: Як створити екземпляр класу і як додати в нього конструктора?

Якщо ви не визначили конструктор копіювання, то використовується конструктор за замовчанням, який створює в новому місці точну копію об'єкта. Ви ж не визначили його (порушили правило трійки)? Ну то і маєте -

int length;
char *str;

копіюються в нове місце. Тобто вказівник - новий, а те, на що він вказує - старе. І деструктори для обох об'єктів намагаються їх звільнити, що ви і спостерігаєте в кінці. Правило трійки не дарма придумали.
Чому там різні посилання - не знаю, доки не побачу код. У мене купа warining-ів і однакові посилання.
http://ideone.com/P8PHgY

85

Re: Як створити екземпляр класу і як додати в нього конструктора?

koala написав:

Якщо ви не визначили конструктор копіювання, то використовується конструктор за замовчанням, який створює в новому місці точну копію об'єкта. Ви ж не визначили його (порушили правило трійки)? Ну то і маєте -

int length;
char *str;

копіюються в нове місце. Тобто вказівник - новий, а те, на що він вказує - старе. І деструктори для обох об'єктів намагаються їх звільнити, що ви і спостерігаєте в кінці. Правило трійки не дарма придумали.
Чому там різні посилання - не знаю, доки не побачу код. У мене купа warining-ів і однакові посилання.
http://ideone.com/P8PHgY

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

#include <cstdlib>
#include <iostream>
#include <cstdio>
#include <vector>

class MyString
{
public:
    MyString::MyString(char* str)
    {
        int length = Length(str);
        char* s1 = new char[length+1];
        for (int i = 0; i < length; i++)
        {
            s1[i] = str[i];
        }
        s1[length] = '\0';
        MyString::str = s1;
    }
    MyString::~MyString()
    {
        std::cout << "deleting at: " << &str << std::endl;
        delete[] str; //очищуємо місце, котре виділялось під оті ваші стрічки
    }

    friend std::ostream& operator<<(std::ostream& os, const MyString& ms);

    void operator+=(char* str)
    {
        int length = Length() + Length(str); // отримуємо довжину нової строки
        char *s = new char[length+1]; // для термінатора
        for (int i = 0; i < Length(); i++)// записуємо в нову строку те, що вже було
        {
            s[i] = MyString::str[i];
        }
        for (int i = 0; i < Length(str); i++)// дописуємо те, що треба додати
        {
            s[i + Length()] = str[i];
        }
        delete[] MyString::str;
        MyString::str = s;// присвоюємо старому вказівнику на стару
        MyString::str[length] = '\0'; //- це термінатор
    }

    int Find(char* subString)
    {
        int subStringLength = Length(subString);
        for (int i = 0; i < Length(); i++)
        {
            int index = -1;
            for (int j = 0; j < subStringLength; j++)
            {
                if (str[i] == subString[j])
                {
                    if (j == 0)
                    {
                        index = i;
                    }                    
                    if (j == subStringLength - 1)
                    {
                        return index;
                    }
                    else
                    if (i != Length() - 1)
                    {
                        i++;
                        continue;
                    }
                }
                else
                {
                    break;
                }
            }
        }
        return -1;
    }

    void Remove(int startIndex, int count)
    {
        int newStrLength = Length() - count;
        char* newStr = new char[newStrLength+1];

        for (int i = 0; i < newStrLength; i++)
        {
            if (i < startIndex)
                newStr[i] = str[i];
            else
                newStr[i] = str[i + count];
        }

        newStr[newStrLength] = '\0';
        delete[] str;
        str = newStr;
    }

    int Length()
    {
        int i = 0;
        while (str[i]!='\0')
        {
            i++;
        }
        return i;
    }

    int Length(char* str)
    {
        int i = 0;
        while (str[i] != '\0')
        {
            i++;
        }
        return i;
    }

private:
    char *str;
};

std::ostream& operator<<(std::ostream& os, const MyString& ms)
{
    os << ms.str;
    return os;
}


void show(int* a)
{
    std::cout << *a << std::endl;
}

int main()
{
    MyString mys = "abcdabe";
    std::cout << mys << std::endl;
    
    MyString mys1 = mys;

    std::cout << mys1 << std::endl;

    system("pause >> void");
}

86

Re: Як створити екземпляр класу і як додати в нього конструктора?

От так написав

MyString::MyString(MyString& copy)
    {
        int length = copy.Length();
        str = new char[length + 1];

        for (int i = 0; i < length; i++)
        {
            str[i] = copy.str[i];
        }

        str[length] = '\0';
    }

Працює шикарно.
А навіщо оті конструктори переміщення, оператор присвоєння переміщення, і оператор присвоєння?
Адже конструктор копіювання наче прекрасно справляється зі своєю задачею.

87

Re: Як створити екземпляр класу і як додати в нього конструктора?

FakiNyan написав:

А навіщо...  оператор присвоєння?
Адже конструктор копіювання наче прекрасно справляється зі своєю задачею.

Оператор присвоєння працює в усіх конструкціях a = b, окрім проголошення, де

MyString m1 = m2;

є синонімом

MyString m1( m2 );

Я б порадив використовувати першу конструкцію для простих типів, а другу для класів, щоб не плутатися.

FakiNyan написав:

оті конструктори переміщення, оператор присвоєння переміщення

розгляньте ситуацію
MyClass MyClass::operator + ( const MyClass& operand ) const;
...
MyClass a = 1, b = 1, c = a + b;
Тут створюється (для повернення значення з operator +) тимчасовий об'єкт типу MyClass, який здохне одразу після копіювання в c. Тому варто не копіювати значення з нього, а просто перебирати - у вашому випадку це буде (так писати трохи неправильно, але для розуміння поки так)

MyString(MyString&& copy)
{
  str = copy.str;
}

І розберіться з модифікатором const - це дуже важлива річ, коли мова йде про посилання.

Подякували: FakiNyan1

88

Re: Як створити екземпляр класу і як додати в нього конструктора?

Надибав на хабрі статтю про constr.
Але мені не дає покою одна фраза

Существует мнемоническое правило, позволяющее легко запомнить, к чему относится const. Надо провести черту через "*", если const слева, то оно относится к значению данных; если справа — к значению указателя.

Мені здається, що тут написали прямо навпаки.
Адже, коли ми пишем от так

char * c = "a";
c="b"; // ми змінюємо данні

А от тут,

char * c = "a";
char * b = "d";
c=b; // ми змінюємо покажчик

І якщо ми пишемо

const char* a = "b";// то ми не зможемо змінити покажчик, але зможемо змінити дані
char* const b = "c"; //а от тут ми можемо змінити лише покажчик, а не дані

89

Re: Як створити екземпляр класу і як додати в нього конструктора?

FakiNyan написав:
char * c = "a";
c="b"; // ми змінюємо данні

А от тут,

char * c = "a";
char * b = "d";
c=b; // ми змінюємо покажчик

ні, в обох випадках ми змінюємо вказівник - по-перше, тому, що c - це вказівник, і вираз c = щось змінює саме c, тобто вказівник; по-друге, щоб змінити дані, ми маємо щось кудись записати, а де те "кудись" в першому випадку знаходиться?
Щоб змінити дані, ми маємо встановити вказівник на щось, що можна змінювати, і зробити *c = 'x'.

FakiNyan написав:

І якщо ми пишемо

const char* a = "b";// то ми не зможемо змінити покажчик, але зможемо змінити дані
char* const b = "c"; //а от тут ми можемо змінити лише покажчик, а не дані

Читаємо: a - це вказівник на const char. Тобто можна змінити вказівник, не можна те, на що він вказує. А b - це константний вказівник на char: можна змінити дані (хоча не в цьому випадку, чому - подумайте), але не можна вказівник.

90 Востаннє редагувалося FakiNyan (28.09.2014 19:47:27)

Re: Як створити екземпляр класу і як додати в нього конструктора?

Ну не знаю, що у вас там не може змінитись в цьому випадку, але в мене змінюється.

Прихований текст

http://не-дійсний-домен/bRtmm/581957c261.png

UPD: я заплтувався

Прихований текст

http://не-дійсний-домен/bRtBk/bf358c92b7.png

91

Re: Як створити екземпляр класу і як додати в нього конструктора?

Прочитайте у мене ще раз, як змінити дані, а як - вказівник.

92

Re: Як створити екземпляр класу і як додати в нього конструктора?

koala написав:

Прочитайте у мене ще раз, як змінити дані, а як - вказівник.

Щоб змінити дані, ми маємо встановити вказівник на щось, що можна змінювати, і зробити *c = 'x'.

Ну от я ж так і роблю. Оте "c"

char* const b = "c";

записується в readonly пам'ять?

93

Re: Як створити екземпляр класу і як додати в нього конструктора?

Що, взагалі, робиться при

char* b = "c";

Десь, в ділянці пам'яті з'являється "c", і ще десь з'являється пам'ять віділена під адресу цього "c"? І "b" має адресу ділянки пам'яті, в котрій знаходиться адреса "c"?
кожна клітинка == 1 байт

Прихований текст

http://не-дійсний-домен/bRuIJ/d35b4526fb.png

94

Re: Як створити екземпляр класу і як додати в нього конструктора?

Ну все ж навпаки...
char* const b = "c"; - десь (в області констант, чи іншій, за бажанням компілятора) в пам'яті є два байти 'c' '\0', і b вказує на них. Це ініціалізація, а не присвоювання, присвоювання
b = a;
призведе до помилки компіляції, вказівник же константний. А присвоювання
*b = 'a';
може призвести до помилки часу виконання, бо буде спроба записати 'a' поверх отого 'c', який невідомо де.

95

Re: Як створити екземпляр класу і як додати в нього конструктора?

koala написав:

Ну все ж навпаки...
char* const b = "c"; - десь (в області констант, чи іншій, за бажанням компілятора) в пам'яті є два байти 'c' '\0', і b вказує на них. Це ініціалізація, а не присвоювання, присвоювання
b = a;
призведе до помилки компіляції, вказівник же константний. А присвоювання
*b = 'a';
може призвести до помилки часу виконання, бо буде спроба записати 'a' поверх отого 'c', який невідомо де.

ммм, ну наче зрозуміло
а чому от тут така фігня?
https://сайт-злодій/pr/src/390807/14119284898930.png
Як це один вказівник вказує на два різних значення?

96 Востаннє редагувалося koala (28.09.2014 20:28:43)

Re: Як створити екземпляр класу і як додати в нього конструктора?

FakiNyan написав:

https://сайт-злодій/pr/src/390807/14119284898930.png

У вас це так виконується? Точно? Чи ви забули перекомпілювати?

97

Re: Як створити екземпляр класу і як додати в нього конструктора?

koala написав:
FakiNyan написав:

https://сайт-злодій/pr/src/390807/14119284898930.png

У вас це так виконується? Точно? Чи ви забули перекомпілювати?

Точно, перевірив тільки що. Можу відиво записати, записати?

98

Re: Як створити екземпляр класу і як додати в нього конструктора?

This doesn't make any sense.
Наведіть скрін властивостей .exe-файла - зокрема, часу створення.

99 Востаннє редагувалося FakiNyan (28.09.2014 20:48:41)

Re: Як створити екземпляр класу і як додати в нього конструктора?

koala написав:

This doesn't make any sense.
Наведіть скрін властивостей .exe-файла - зокрема, часу створення.

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

Прихований текст

http://не-дійсний-домен/bRyGQ/b7f9cc6d57.png
p.s. аа, то воно виводить ыррыыр, бо воно ж тільки латинські літери зара може виводити

100

Re: Як створити екземпляр класу і як додати в нього конструктора?

Можу тільки порадити викинути це лайно на смітник і поставити собі компілятор :(