Операция new

Доброго времени суток всем!!!
Имею вот такой вопрос. Вот код

#include <iostream>
#include <cstring>

struct stringy{
    char * str;
    int ct;
};

void set(stringy & st, char * s);
void show(const stringy & st, int n = 1);
void show(const char * test, int n = 1);

int main()
{
    stringy beany;
    char testing[] = "Reality isn't what it used to be.";

    set(beany, testing);

    show(beany);
    show(beany, 2);

    testing[0] = 'D';
    testing[1] = 'u';

    show(testing);
    show(testing, 3);
    show("Done!");

    return 0;
}

void set(stringy & st, char * s)
{
    int len = strlen(s);
    st.str = new char[len];
    strcpy(st.str, s);
    st.ct = len;
}

void show(const stringy & st, int n)
{
    while(n--)
        std::cout << st.str << std::endl;
}

void show(const char * test, int n)
{
    while(n--)
        std::cout << test << std::endl;
}

ВОПРОС: Где освобождается память, выделенная в функции void set(stringy & st, char * s) ?

В программе — нигде.
Да и вообще у Вас там выход за пределы выделенной памяти

int len = strlen(s);
st.str = new char[len]; //Выделили память под len символов
strcpy(st.str, s); //Скопировали в приемник len символов + завершающий 0

Но память то выделена при помощи new?

память то выделена при помощи new

Память, выделенная при помощи new, освобождается только при помощи delete или после завершения программы.

Правильно, porshe! Применение new должно сопровождаться вызовом delete. Повторю вопрос.
ВОПРОС: Где в данной программе нужно вызвать операцию delete?
(Выход за пределы массива исправлен)

#include <iostream>
#include <cstring>

struct stringy{
    char * str;
    int ct;
};

void set(stringy & st, char * s);
void show(const stringy & st, int n = 1);
void show(const char * test, int n = 1);

int main()
{
    stringy beany;
    char testing[] = "Reality isn't what it used to be.";

    set(beany, testing);

    show(beany);
    show(beany, 2);

    testing[0] = 'D';
    testing[1] = 'u';

    show(testing);
    show(testing, 3);
    show("Done!");

    return 0;
}

void set(stringy & st, char * s)
{
    const int len = strlen(s);
    st.str = new char[len + 1];
    strcpy(st.str, s);
    st.ct = len;
}

void show(const stringy & st, int n)
{
    while(n--)
        std::cout << st.str << std::endl;
}

void show(const char * test, int n)
{
    while(n--)
        std::cout << test << std::endl;
}

Правильно, porshe

А я даже не знал, что это было задание, я думал это был вопрос :)

Где в данной программе нужно вызвать операцию delete?

Вообще, её здесь негде применять, дабы не потерять значение beany. Но уж если сильно хочется, то можно создать функцию _free(), которая освободит память, и присвоит beany.str = NULL.

P.S.: А почему бы не сделать класс? Будет ведь удобнее, и освобождением будет заниматься деструктор.

Чёрт, понял в чём тут фишка.
Освобождать память нужно в начале функции set, что бы избежать утечки памяти.
То есть как то так:


void set(stringy & st, char * s)
{
    if ( st.str )
        delete []st.str; 
    const int len = strlen(s);
    st.str = new char[len + 1];
    strcpy(st.str, s);
    st.ct = len;
}

При условии, что пустая st.str = NULL( можно задать в конструкторе, например ).

можно без if.

То есть, если попытаться освободить память по адресу 0, то ничего не произойдёт?

То есть, если попытаться освободить память по адресу 0, то ничего не произойдёт?

Ничего.

Ребята, программа не компилится ???

!!! «Access violation reading location 0xccccccc0.».

Да и не логично удалять в функции void set(stringy & st, char * s) память при помощи delete перед тем как функция выделяет память при помощи new.

Да и не логично удалять в функции void set(stringy & st, char * s) память при помощи delete перед тем как функция выделяет память при помощи new.

логично.

#include <iostream>
#include <cstring>

struct stringy{
    char * str;
    int ct;
};

void init ( stringy & ) ;
void set(stringy & st, char * s);
void release ( stringy & ) ;
void show(const stringy & st, int n = 1);
void show(const char * test, int n = 1);

int main()
{
    stringy beany;
    init (beany) ;
    char testing[] = "Reality isn't what it used to be.";

    set(beany, testing);

    show(beany);
    show(beany, 2);

    testing[0] = 'D';
    testing[1] = 'u';

    show(testing);
    show(testing, 3);
    show("Done!");
    release ( beany ) ;
    return 0;
}

void init(stringy & st )
{
    st.str = NULL ;
    st.ct = 0 ;
}

void release(stringy & st )
{
    delete [] st.str ;
    st.str = NULL ;
    st.ct = 0 ;
}

void set(stringy & st, char * s)
{
    int len = strlen(s);
    char * p = new char[len + 1];
    strcpy(p, s);
    release ( st ) ;
    st.str = p ;
    st.ct = len;
}

void show(const stringy & st, int n)
{
    while(n--)
    std::cout << st.str << std::endl;
}

void show(const char * test, int n)
{
    while(n--)
    std::cout << test << std::endl;
}

Да и не логично удалять в функции void set(stringy & st, char * s) память при помощи delete перед тем как функция выделяет память при помощи new.

А как ещё избежать утечки памяти?

А как ещё избежать утечки памяти?

делать нормальный класс. А еще лучше пользоваться готовым :)

Резюмируя вышесказанное...

(0) В исходном варианте кода память освобождается только при завершении программы. Для того, что бы освободить память в программе, перед return 0 надо вставить оператор delete [] beany.str;. Но это не очень красивый вариант.

(1) Захват, перераспределение и освобождение ресурсов должно происходить на одном уровне абстракции. Это [должно быть!] общеизвестное правило. Если уровень абстракции функции, как в данном случае, то вариант от Croessmah как раз удовлетворяет этому правилу. Если уровень абстракции класс, то работа с ресурсами должна происходить на уровне методов класса.

(2) Вариант от Croessmah фактически реализует абстракцию на уровне класса. void init(stringy & st ) — конструктор, void release(stringy & st ) — деструктор, void set(stringy & st, char * s) — метод-сеттер или оператор присваивания, void show(const stringy & st, int n) — почти метод-геттер. Конечно, что бы привести этот код к виду нормального класса, требуется серьёзная доработка (для того, что бы во всех ситуациях класс (экземпляр класса) работал ожидаемым образом.

Кстати, хорошая тема для новой «разминки для мозгов»: аккуратно написать класс, реализующий строку со счётчиком (или «строка в стиле Pascal») без использования STL и других библиотек. Примечание: строка со счётчиком НЕ завершается '\0'.

хорошая тема для новой «разминки для мозгов»

увы, эта тема бесконечна и не имеет решения :)

Череп, а подробнее, что за строка со счетчиком?
И если есть время посмотри мой первый класс :)

Croessmah, почему всё так пессимистично? Тема вполне исчерпаемая, если не усложнять себе задачу абсолютной переносимостью кода в сочетании с абсолютной эффективностью, поддержкой многобайтовых кодировок и неограниченным объёмом строки.

В данном случае, смысл «разминки» заключался бы в аккуратном написании класса с динамическим захватом и освобождением ресурсов (памяти) и с минимальной функциональностью. Ключевое слово «аккуратно». Задачка достаточно простая, но есть подводные камни. В плане повышения собственного скилла — полезно.

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.