61

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

quez написав:
FakiNyan написав:
0x9111A написав:

Мені чомусь здається що деструктор силою викликати тут було поганою ідеєю

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

Додай до деструктора якийсь вивід, виведе два рази.

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

62

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

FakiNyan написав:

Тобто мені тепер тре в конструкторі створювати масив в купі використовуючи new, копіювати в нього масив з стеку, котрий ми отримуємо як аргумент, та присвоїти str'у вказівник на той новий масив в купі, і тоді str завжди буде вказувати на дані  створені за допомогою new, ага?

100%.
Тільки масив не зі стеку - в стек іде посилання на масив, а де він сам знаходиться, ми не знаємо. Ім'я масиву майже завжди (крім оператора sizeof) повертає посилання на початковий елемент: a == &a[ 0 ].

63

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

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

Тобто мені тепер тре в конструкторі створювати масив в купі використовуючи new, копіювати в нього масив з стеку, котрий ми отримуємо як аргумент, та присвоїти str'у вказівник на той новий масив в купі, і тоді str завжди буде вказувати на дані  створені за допомогою new, ага?

100%.
Тільки масив не зі стеку - в стек іде посилання на масив, а де він сам знаходиться, ми не знаємо. Ім'я масиву майже завжди (крім оператора sizeof) повертає посилання на початковий елемент: a == &a[ 0 ].

Я щось заплутався. Скільки видів в пам'яті у нас взагалі є?
Куча, стек, і все?
Чому, коли я робив от так

char* str = "sdfsd";

То я не міг змінювати елементи масиву, в якій пам'яті вони були?

64 Востаннє редагувалося Arete (21.09.2014 13:53:38)

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

FakiNyan написав:

Я щось заплутався. Скільки видів в пам'яті у нас взагалі є?
Куча, стек, і все?
Чому, коли я робив от так

char* str = "sdfsd";

То я не міг змінювати елементи масиву, в якій пам'яті вони були?

Просто запам’ятай:
  якщо об’єкт створюється оператором new, то його потрібно видаляти оператором delete, і тільки один раз(!)
  оператором delete можна видаляти тільки об’єкти, створені оператором new, інші - не можна.

char* str = "sdfsd";
"sdfsd" еквівалентно const char [] {'s','d','f','s','d','\0'}

В цьому випадку "sdfsd" є константний строковий літерал, тому що він забитий в коді "руками". А str - вказівник на цей літерал.

FakiNyan написав:

Я щось заплутався. Скільки видів в пам'яті у нас взагалі є?
Куча, стек, і все?

І все, принаймні я про інші види пам’яті ніколи не чув.

Подякували: FakiNyan, 0xDADA11C72

65

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

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

Я щось заплутався. Скільки видів в пам'яті у нас взагалі є?
Куча, стек, і все?
Чому, коли я робив от так

char* str = "sdfsd";

То я не міг змінювати елементи масиву, в якій пам'яті вони були?

Просто запам’ятай:
  якщо об’єкт створюється оператором new, то його потрібно видаляти оператором delete, і тільки один раз(!)
  оператором delete можна видаляти тільки об’єкти, створені оператором new, інші - не можна.

char* str = "sdfsd";
"sdfsd" еквівалентно const char [] {'s','d','f','s','d','\0'}

В цьому випадку "sdfsd" є константний строковий літерал, тому що він забитий в коді "руками". А str - вказівник на цей літерал.

FakiNyan написав:

Я щось заплутався. Скільки видів в пам'яті у нас взагалі є?
Куча, стек, і все?

І все, принаймні я про інші види пам’яті ніколи не чув.

то константи теж в кучі робляться, чи нє?

66

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

Ще статична пам’ять існує, там якраз константи і сидять

Подякували: Arete, FakiNyan, koala3

67 Востаннє редагувалося FakiNyan (21.09.2014 15:30:04)

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

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

class MyString
{
public:
    int length;
    char *str;
    MyString::MyString(char* str)
    {
        int length = strlen(str);  // ДУЖЕ ВАЖЛИВИЙ РЯДОК
        char* s1 = new char[length+1];  // ДУЖЕ ВАЖЛИВИЙ РЯДОК
        for (int i = 0; i < length; i++)  // ДУЖЕ ВАЖЛИВИЙ РЯДОК
        {
            s1[i] = str[i];  // ДУЖЕ ВАЖЛИВИЙ РЯДОК
        }
        s1[length] = '\0';  // ДУЖЕ ВАЖЛИВИЙ РЯДОК
        MyString::str = s1;  // ДУЖЕ ВАЖЛИВИЙ РЯДОК
        RecalcLength();
    }
    MyString::~MyString()
    {
        delete[] str; //очищуємо місце, котре виділялось під оті ваші стрічки
    }

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

    void operator+=(char* str)
    {
        int length = MyString::length + strlen(str); // отримуємо довжину нової строки
        char *s = new char[length+1]; // для термінатора
        for (int i = 0; i < MyString::length; i++)// записуємо в нову строку те, що вже було
        {
            s[i] = MyString::str[i];
        }
        for (int i = 0; i < strlen(str); i++)// дописуємо те, що треба додати
        {
            s[i + MyString::length] = str[i];
        }
       delete[] MyString::str; // ДУЖЕ ВАЖЛИВИЙ РЯДОК
        MyString::str = s;// присвоюємо старому вказівнику на стару
        MyString::str[length] = '\0'; //- це термінатор
        MyString::length = length;// запам'ятовуємо довжину нової стрічки
    }
private:
    void RecalcLength()
    {
        MyString::length = strlen(MyString::str);
    }
};

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

int main()
{
    { MyString mys = "abc";
    
    for (int i = 0; i < 5000; i++)
    {
        mys += "a";
        system("cls");
        std::cout << i << std::endl;
    }
    char* s = "abc";
    std::cout << sizeof(s[1]) << std::endl;
    std::cout << mys << std::endl;
    }
    system("pause >> void");
}

Отак зробив, важливі рядки я обклав коментарем.
І знаєте що? Тепер все працює дуже файно, і ніякого витіку пам'яті немає, а знаєте, як я це перевірив?
Просто порівняв, скільки програма використовує пам'яті зара, і скільки раніше використовувала.
Зараз збільшення пам'яті майже не видно, декілька кілобайт лише. Після спрацювання циклу.
А раніше використовувана пам'ять збільшувалась з 0.4мб до 49мб!

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

68

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

Тепер запхайте дані в private. RecalcLength в конструкторі зайвий - ви і так length+1 виділили, хіба ви того не знаєте? Власне, можете навіть зайвої змінної не створювати - все одно вона зберігає довжину рядку. Ну і якщо ви користуєтеся strlen, дивно не користуватися strcpy (я до того, щоб не користуватися strlen).
А так - все чудово.

69

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

koala написав:

Тепер запхайте дані в private. RecalcLength в конструкторі зайвий - ви і так length+1 виділили, хіба ви того не знаєте? Власне, можете навіть зайвої змінної не створювати - все одно вона зберігає довжину рядку.

То ви про змінну MyString::length?

koala написав:

Ну і якщо ви користуєтеся strlen, дивно не користуватися strcpy (я до того, щоб не користуватися strlen).

А як же я без використання strlen і без зберігання довжини в змінну, буду дізнаватись про довжину рядка?
Тут же або без strlen, але з змінною, або без змінної, але з strlen.

70

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

FakiNyan написав:

То ви про змінну MyString::length?

І про MyString::str також.

FakiNyan написав:

А як же я без використання strlen і без зберігання довжини в змінну, буду дізнаватись про довжину рядка?
Тут же або без strlen, але з змінною, або без змінної, але з strlen.

Стандартні рядки в C, і, відтак, в C++ - це рядки, обмежені нулем (null-terminated string, ASCIIZ). І ви це самі чудово знаєте, бо додаєте цей самий '\0' в свій рядок. Ви не знаєте, як знайти в масиві перший елемент, що має певне значення?

71

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

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

То ви про змінну MyString::length?

І про MyString::str також.

FakiNyan написав:

А як же я без використання strlen і без зберігання довжини в змінну, буду дізнаватись про довжину рядка?
Тут же або без strlen, але з змінною, або без змінної, але з strlen.

Стандартні рядки в C, і, відтак, в C++ - це рядки, обмежені нулем (null-terminated string, ASCIIZ). І ви це самі чудово знаєте, бо додаєте цей самий '\0' в свій рядок. Ви не знаєте, як знайти в масиві перший елемент, що має певне значення?

Знаю... але хіба той strlen робить не те саме?

72

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

Звісно, що те саме. А strcpy робить те саме, що й три цикли (один в конструкторі і два в operator +=) у вашій програмі. Тому я й кажу: або хрестика зніміть, або труси одягніть. Або використовуйте всю бібліотеку cstring, або не використовуйте взагалі.

Подякували: 0x9111A1

73 Востаннє редагувалося FakiNyan (22.09.2014 08:22:50)

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

koala написав:

Звісно, що те саме. А strcpy робить те саме, що й три цикли (один в конструкторі і два в operator +=) у вашій програмі. Тому я й кажу: або хрестика зніміть, або труси одягніть. Або використовуйте всю бібліотеку cstring, або не використовуйте взагалі.

то я краще не буду її використовувати. я ж мав би написати свою cstring
я просто думав, шо ота strlen - це щось типу такої функції наднизького рівня, котра є самою основною, і котра використовується у всіх-всіх класах і методах

74

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

strlen - функція того самого рівня, що й strcpy і решта функцій з <cstring>. C++ - мова модульна до неймовірності, не хочете використовувати рядки - не використовуйте взагалі, не питання.

75

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

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

не хочете використовувати рядки - не використовуйте взагалі, не питання.

?

76

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

До того, що strlen - не "функція наднизького рівня", а просто функція з бібліотеки. Ви маєте право її (бібліотеку) не використовувати. Але якщо використовуєте - то абсурдно користуватися тільки однією функцією з цілої бібліотеки.

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

77

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

koala написав:

До того, що strlen - не "функція наднизького рівня", а просто функція з бібліотеки. Ви маєте право її (бібліотеку) не використовувати. Але якщо використовуєте - то абсурдно користуватися тільки однією функцією з цілої бібліотеки.

так то я давно пойняв

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

півгодини тому

78 Востаннє редагувалося FakiNyan (22.09.2014 18:51:33)

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

Я вам пакушать приніс.
Значить, зробив свою strlen, зветься Length.
Додав метод Remove, котрий видаляє в нашій стрічці непотрібну підстроку.
Намучався я з ним... основною проблемою було вірно підставляти індекси в циклах, плутався я там часто, під кінець щось накльоцав і воно запрацювало.

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

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()
    {
        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'; //- це термінатор
    }

    void Remove(char* charsToRemove)
    {
        int index = 0;
        std::cout << "Remove's entry..." << std::endl;
        int lengthToRemove = Length(charsToRemove); // дізнаємось про довжину строки, котру треба видалити
        std::cout << "Length to remove: " << lengthToRemove << std::endl;
        for (int i = 0; i < Length(); i++) //починається цикл пошуку потрібної підстроки в нашій головній стрічці
        {
            for (int j = 0; j < lengthToRemove; j++) //перевірка, чи співпадає поточний знак головної строки - першому знаку строки для видалення
            {
                if (str[i] != charsToRemove[j])// якщо не співпадає, то прериваємо цей підцикл, та йдемо далі по головній стрічці
                {
                    std::cout << "Wrong character comparsion:" << str[i] << " and " << charsToRemove[j] << std::endl;
                    break;
                }
                else // якщо співпадає, то йдемо далі
                {
                    if (j == 0) // якщо поточний знак головної строки співпадає саме з першим знаком підстроки для видалення
                    { 
                        index = i; // запам'ятовуємо індекс цього знаку (можливо, цей індекс вказує на початок підстроки)
                    }
                    std::cout << "Something found... " << j+1 << " need all "<<lengthToRemove <<  std::endl;


                    if (i < Length()) //якщо ми не в кінці головної строки
                    {

                        i++; //то збільшуємо індекс головного циклу, таким чином ми йдемо до перевірки наступного знаку головної строки
                    }
                    else
                    {
                        break; // якщо ж ми не знайшли цілу підстроку, то капець, прериваємо цикл
                    }

                    if (j == lengthToRemove - 1) //якщо ми знайшли цілу підстроку в нашій головній стрічці
                    {
                        std::cout << "I have got it!" << std::endl;
                        int newStrLength = Length() - lengthToRemove; // рахуємо довжину нової строки (якою вона буде після операції видалення)
                        std::cout <<"New string will have length: "<< newStrLength  << std::endl;

                        char* newStr = new char[newStrLength+1]; // виділяємо пам'ять під цю строку, +1 для ТЕРМІНАТОРА!

                        std::cout << "First cycle has going on" << std::endl;

                        if (index > 0){ // якщо індекс початку підстроки більше нуля
                            for (int k = 0; k < index; k++) //цей цикл буде заповнювати нову підстроку тими знаками, котрі йдуть перед нею в головній стрічці
                            {
                                std::cout << "k is: " << k << "  index is: " << index << std::endl;

                                std::cout << "At " << k << " index we have: " << str[k] << std::endl;

                                newStr[k] = str[k];
                            }

                            std::cout << "Second cycle has going on" << std::endl;

                            for (int l = index; l < Length()-((lengthToRemove-2)+index); l++) // а цей цикл заповнює нову підстроку знаками, що йдуть після підстроки в головній стрічці
                            {
                                std::cout << "At " << l << " index we have: " << str[l+index+(lengthToRemove-2)] << std::endl;//я не знаю, як воно працює, просто натикав щось навмання =(
                                newStr[l] = str[l + index + (lengthToRemove - 2)];
                            }
                        }
                        else //якщо ж підстрока починається з 0 індексу в головній стрічці
                        {
                            for (int h = 0; h < newStrLength; h++)//цей цикл просто заповнює нову строку тим, що йде після підстроки в головній стрічці
                            {
                                std::cout << "At " << h << " index we have: " << str[h+lengthToRemove] << std::endl;
                                newStr[h] = str[h + lengthToRemove];
                            }
                        }
                        newStr[newStrLength] = '\0'; // ставимо ТЕРМІНАТОРА!
                        delete[] str; //звільнюємо місце старої стрічки
                        str = newStr; //запам'ятовуємо посилання на нову
                        std::cout << "Done!" << std::endl;

                    }
                }
            }
        }
    }

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

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

int main()
{
    MyString mys = "abcde";
    mys.Remove("abc");
    std::cout << mys << std::endl;
    std::cout << "Length: " << mys.Length() << std::endl;

    system("pause >> void");
}

Тільки от воно видаляє лише першу знайдену підстроку.
Майбуть, час для рекурсії?

79 Востаннє редагувалося koala (22.09.2014 20:04:10)

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

Не хочу навіть читати той код. Переробіть на

int MyString::Find(MyString);//знайти номер символу, з якого починається підрядок, певний аналог strstr
void MyString::Remove(int begin, int count);//видалити до count символів, починаючи з символу номер begin

Після того ваш Remove стане записуватися в два рядки.

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

80

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

В мене вчора, видно, зовсім дах поїхав, якщо я видаляв стрічку з використанням двох циклів і незрозумілим нагромадженням усіляких видозмінених індексів.
От, зробив одним циклом видалення, аж плачу від того, наскільки це просто та компактно в порівнянні з вчорашнім кодом, про Find мовчу...

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

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()
    {
        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;
}

int main()
{
    MyString mys = "abcdabe";
    std::cout << mys << std::endl;
    char* stringToRemove = "bcd";
    int index = mys.Find(stringToRemove);
    if (index != -1)
    {
        mys.Remove(index, mys.Length(stringToRemove));
    }

    std::cout <<mys<< std::endl;

    system("pause >> void");
}
Подякували: Arete1