Модераторы: bsa
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> double free в деструкторе, почему-то деструктор вызывается чаще 
:(
    Опции темы
marsh123
Дата 1.3.2012, 23:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.6.2008

Репутация: нет
Всего: нет



Доброй ночи.

Перехожу на C++ с СИ и тут не понимаю одной тонкости.

Если по-порядку, то пытаюсь написать класс строк, а затем класс стека на этих строках, всё это нужно сделать без использования STL.

Проблема в том, что при запуске возникает аварийный останов, с ошибкой double free or corruption, поставив cout в деструктор, я заметил, что он действительно вызывается больше раз, чем я этого ожидаю.

Ниже приведу код, просьба пояснить, в чём косяк и строго не судить, я совершаю плавный переход на плюсы  smile 

Код

#include <iostream>
#include <stdlib.h>

using namespace std;

class String {
private:
    char *data;
    int capacity; //заполненность
    int size; //размер
    
public:
    String();
    String(const char* str);
    operator string () const;
    void operator =(const char *str);
    void operator =(String& str);
    void Add_Symb(char symb);
    ~String();
};

String::String() {
    capacity = 0;
    size = 10;
    data = (char*)malloc(sizeof(char)*size);
    data[0] = '\0';
}

String::String(const char* str) {
    int i = 0;
    capacity = 0;
    size = 10;
    data = (char*)malloc(sizeof(char)*size);
    while (str[i] != '\0') {
        Add_Symb(str[i]);
        i++;
    }
    Add_Symb('\0');
}

String::operator string () const {
    return data;
}

void String::operator =(const char* str) {
    free(data);
    int i = 0;
    capacity = 0;
    size = 10;
    data = (char*)malloc(sizeof(char)*size);
    while (str[i] != '\0') {
        Add_Symb(str[i]);
        i++;
    }
    Add_Symb('\0');
}

void String::operator =(String& str) {
    free(data);
    int i = 0;
    capacity = 0;
    size = 10;
    data = (char*)malloc(sizeof(char)*size);
    while (str.data[i] != '\0') {
        Add_Symb(str.data[i]);
        i++;
    }
    Add_Symb('\0');
}

void String::Add_Symb(char symb) {
    if (capacity >= size) {
        size += 10;
        data = (char*)realloc(data, sizeof(char)*size);
    }
    data[capacity++] = symb;
}

String::~String() {
    cout << "string destructor\n";
    free(data);
}

typedef struct stack_elem stack_elem;

struct stack_elem {
    String data;
    stack_elem *next;
};

class Stack {
private:
    stack_elem *head;
public:
    Stack();
    void Push(String str);
    String Pop();
    void Print();
    ~Stack();
};

Stack::Stack() {
    head = NULL;
}

void Stack::Push(String str) {
    stack_elem *e = (stack_elem*)malloc(sizeof(stack_elem));
    if (head == NULL) {
        head = e;
        head->data = str;
        head->next = NULL;
        return;
    }
    e->next = head;
    e->data = str;
    head = e;
}

String Stack::Pop() {
    String str;
    if (head == NULL) return str;
    str = head->data;
    stack_elem *tmp = head;
    head = head->next;    
    free(tmp);
    return str;
}

void Stack::Print() {
    stack_elem *cur = head;
    while (cur != NULL) {
        cout << string(cur->data) << " ";
        cur = cur->next;
    }
    cout << "\n";
}

Stack::~Stack() {
    stack_elem *cur = head;
    stack_elem *tmp;
    while (cur != NULL) {
        tmp = cur;
        cur = cur->next;
        free(tmp);
    }
}

int main() {
    String mystr, mystr2;
    mystr = "fkdlskldfs";
    mystr2 = "dfkdfk";
    cout << string(mystr) << "\n" << string(mystr2) << "\n";

    Stack mystack;
    mystack.Push(mystr);
    mystack.Push(mystr2);
    mystack.Print();
    return 0;
}

PM MAIL   Вверх
bsa
Дата 1.3.2012, 23:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



замени все malloc/calloc/free на new/delete и new[]/delete[]. Или вызывай размещающий new и деструктор вручную.
Сейчас ты используешь несконструированные объекты: stack_elem *e = (stack_elem*)malloc(sizeof(stack_elem)); не вызывает конструктор для String.
PM   Вверх
marsh123
Дата 2.3.2012, 10:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.6.2008

Репутация: нет
Всего: нет



Цитата(bsa @ 1.3.2012,  23:59)
замени все malloc/calloc/free на new/delete и new[]/delete[]. Или вызывай размещающий new и деструктор вручную.
Сейчас ты используешь несконструированные объекты: stack_elem *e = (stack_elem*)malloc(sizeof(stack_elem)); не вызывает конструктор для String.

Можете показать на примере какой-то строчки кода, как осуществлять замену? Я просто думал, что new можно вызывать только для объекта с конструктором, что мне тогда делать, если нужно выделить память под структуру?
PM MAIL   Вверх
xvr
Дата 2.3.2012, 12:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 35
Всего: 223



Отсуствует copy конструктор, оператор присваивания описан неверно (надо как минимум void operator =(const String& str); )

PM MAIL   Вверх
marsh123
Дата 2.3.2012, 13:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.6.2008

Репутация: нет
Всего: нет



Ладно, спасибо, буду вникать в азы С++ более подробно, запасусь книжками smile
PM MAIL   Вверх
marsh123
Дата 2.3.2012, 16:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.6.2008

Репутация: нет
Всего: нет



Все же спрошу здесь, чтобы до конца разобраться, пока вообще убрал стек, хочу строку по-хорошему дописать, заменил все malloc\free на new\delete, также вообще убрал перегрузку =, так как он, похоже, лишняя, как я понял из литературы, вот такая конструкция:
Код

s1 = "fdjfdj";

Будет работать без всяких перегрузок, нужен лишь конструктор класса объекта s1 с аргументом const char*, как я, собственно, и сделал, он даже вызывается на этой строке, но я получаю сегфолт  smile 

При этом простые вызовы Add_Symb отлично работают, а если я их циклю в конструкторе, то сегфолт  smile 

Укажите, пожалуйста, более подробно на ошибки, буду очень признателен.

Собственно код:
Код

#include <iostream>

using namespace std;

class String {
private:
    char *data;
    int capacity; //заполненность
    int size; //размер
    
public:
    String();
    String(const char* str);
    operator string () const;
    void Add_Symb(char symb);
    ~String();
};

String::String() {
    capacity = 0;
    size = 10;
    data = new char[size];
}

String::String(const char* str) {
    int i = 0;
    while (str[i] != '\0') {
        Add_Symb(str[i]);
        i++;
    }
    Add_Symb('\0');
}

String::operator string () const {
    return data;
}

void String::Add_Symb(char symb) {
    if (capacity >= size) {
        size += 10;
        char *tmp = new char[size];
        for (int i = 0; i < capacity; i++) {
            tmp[i] = data[i];
        }
        delete[] data;
        data = tmp;
    }
    data[capacity++] = symb;
}

String::~String() {
    cout << "destructor\n";
    delete[] data;
}

int main() {
    String s1;
    //s1 = "fdjfdj";
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    s1.Add_Symb('b');
    cout << string(s1) << "\n";
    return 0;
}

PM MAIL   Вверх
xvr
Дата 2.3.2012, 17:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 35
Всего: 223



Вам нужен оператор присваивания (ну и copy конструктор не помешает). На вашей строке s1 = "fdjfdj"; на самом деле происходит следующее (в виде псевдокода, с явными вызовами всех конструкторов и деструкторов):
Код

    String s1;
    s1 = "fdjfdj";

// тут будет так -
 String temporary("fdjfdj");
 s1.operator = (temporary); // оператор присваивания
 temporary.~String(); // деструктор
...
 s1.~String(); // деструктор от s1. 
// Тут и будет sigfault, т.к. вы пытаетесь удалить s1.data, который уже удалили, когда он был temporary.data


PM MAIL   Вверх
bsa
Дата 2.3.2012, 17:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



marsh123, поменяй size и capacity местами. Обычно, capacity означает общий выделенный объем, а size - занятый.
Размер строки при необходимости увеличить, лучше удваивать.
Цитата(marsh123 @  2.3.2012,  17:44 Найти цитируемый пост)
При этом простые вызовы Add_Symb отлично работают, а если я их циклю в конструкторе, то сегфолт
Проблема в том, что конструкторы у тебя инициализируют переменные по разному. В дефолтном ты инициализируешь все, а в другом ничего. 
PM   Вверх
marsh123
Дата 2.3.2012, 17:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.6.2008

Репутация: нет
Всего: нет



Ниже приведу код, даже конструктор копирования добавил, хотя пока слабо понимаю, зачем он, поэтому, возможно, написал его неправильно.

К коду сопутствующий вопрос, оператор =, по-хорошему, еще должен затирать старую строку, то есть нужно как-то освобождать старую память, по всей видимости delete[] делать, но, что если память уже освобождена и я буду пытаться делать delete[] уже освобожденной памяти, как это проверить  smile 

Код

#include <iostream>

using namespace std;

class String {
private:
    char *data;
    int capacity; //заполненность
    int size; //размер
    
public:
    String();
    String(const char *str);
    String(const String &str);
    operator string () const;
    void operator =(const char *str);
    void Add_Symb(char symb);
    ~String();
};

String::String() : capacity(0), size(10) {
    data = new char[size];
}

String::String(const char *str) : capacity(0), size(10) {
    int i = 0;
    data = new char[size];
    while (str[i] != '\0') {
        Add_Symb(str[i]);
        i++;
    }
    Add_Symb('\0');
    --capacity;
}

String::String(const String &str) {
    data = new char[str.size];
    capacity = str.capacity;
    size = str.size;
    for (int i = 0; i < str.capacity; i++) {
        data[i] = str.data[i];
    }
}

String::operator string () const {
    return data;
}

void String::operator =(const char *str) {
    int i = 0;
    while (str[i] != '\0') {
        Add_Symb(str[i]);
        i++;
    }
    Add_Symb('\0');
    --capacity;
}

void String::Add_Symb(char symb) {
    if (capacity >= size) {
        size += 10;
        char *tmp = new char[size];
        for (int i = 0; i < capacity; i++) {
            tmp[i] = data[i];
        }
        delete[] data;
        data = tmp;
    }
    data[capacity++] = symb;
}

String::~String() {
    cout << "destructor\n";
    delete[] data;
}

int main() {
    String s1;
    s1 = "fdjfdj";
    s1.Add_Symb('a');
    s1.Add_Symb('a');
    cout << string(s1) << "\n";
    return 0;
}


Цитата

Обычно, capacity означает общий выделенный объем, а size - занятый.

 smile А Вы правы, вот так дела, а я уже несколько лет считал, что наоборот. Придется перепривыкать чтоли.

Это сообщение отредактировал(а) marsh123 - 2.3.2012, 17:38
PM MAIL   Вверх
marsh123
Дата 2.3.2012, 20:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.6.2008

Репутация: нет
Всего: нет



Последний вопрос отменяется, там разобрался, уже дописал стек и он даже работает  smile, ну или делает вид, что работает.

Реализовал возможность пушить в стек через =, то есть:
Код

Stack s;
s = "abc";

аналогично
Код

String str;
Stack s;
str = "abc";
s.push(str);


Теперь мне нужно сделать, чтобы работало вот так:
Код

Stack s;
s = "abc", "bcd", "aaa";

Всё это должно быть запушено слева направо.

Надо перегрузить запятую (ну и, видимо, равно по-другому как-то перегружать), но идей что-то нет вообще  smile 

Может объясните, как в таком случае перегружают, на примере? Заранее спасибо smile

Код

#include <iostream>
#include <stdlib.h>

using namespace std;

class String {
private:
    char *data;
    int capacity;
    int size;
    
public:
    String();
    String(const char *str);
    String(const String &str);
    operator string () const;
    void operator =(const char *str);
    void operator =(const String &str);
    char& operator [](int index);
    void add_symbol(char symb);
    int lenght();
    ~String();
};

String::String() : capacity(0), size(10) {
    data = new char[size];
}

String::String(const char *str) : capacity(0), size(10) {
    int i = 0;
    data = new char[size];
    while (str[i] != '\0') {
        add_symbol(str[i]);
        i++;
    }
    add_symbol('\0');
    --capacity;
}

String::String(const String &str) {
    data = new char[str.size];
    capacity = str.capacity;
    size = str.size;
    for (int i = 0; i < str.capacity; i++) {
        data[i] = str.data[i];
    }
}

String::operator string () const {
    return data;
}

void String::operator =(const char *str) {
    int i = 0;
    delete[] data;
    capacity = 0;
    size = 10;
    data = new char[size];
    while (str[i] != '\0') {
        add_symbol(str[i]);
        i++;
    }
    add_symbol('\0');
    --capacity;
}

void String::operator =(const String &str) {
    delete[] data;
    data = new char[str.size];
    capacity = str.capacity;
    size = str.size;
    for (int i = 0; i < str.capacity; i++) {
        data[i] = str.data[i];
    }
}

char& String::operator [](int index) {
    return data[index];
}

void String::add_symbol(char symb) {
    if (capacity >= size) {
        size += 10;
        char *tmp = new char[size];
        for (int i = 0; i < capacity; i++) {
            tmp[i] = data[i];
        }
        delete[] data;
        data = tmp;
    }
    data[capacity++] = symb;
}

int String::lenght() {
    return capacity;
}

String::~String() {
    delete[] data;
}

class Stack_elem {
private:
public:
    String data;
    Stack_elem *next;
    Stack_elem():next(NULL) {}
};

class Stack {
private:
    Stack_elem *head;
public:
    Stack():head(NULL) {}
    void push(String str);
    String pop();
    void print();
    void operator =(const char *str);
    ~Stack();    
};

void Stack::push(String str) {
    Stack_elem *e = new Stack_elem();
    e->data = str;
    if (head == NULL) {
        head = e;
        return;
    }
    e->next = head;
    head = e;
}

String Stack::pop() {
    if (head == NULL) {
        cout << "Try to pop from empty stack\n";
        exit(1);
    }
    String tmp = head->data;
    Stack_elem *temp = head;
    head = head->next;
    delete temp;
    return tmp;
}

void Stack::print() {
    Stack_elem *cur = head;
    while (cur != NULL) {
        cout << string(cur->data) << " ";
        cur = cur->next;
    }
    cout << "\n";
}

void Stack::operator =(const char *str) {
    String ss;
    ss = str;
    push(ss);
}

Stack::~Stack() {
    Stack_elem *cur = head;
    Stack_elem *tmp;
    while (cur != NULL) {
        tmp = cur;
        cur = cur->next;
        delete tmp;
    }
}

int main() {
    Stack s;
    s = "fds";
    s = "kkk";
    s.print();
    return 0;
}

PM MAIL   Вверх
bsa
Дата 2.3.2012, 22:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



marsh123, у тебя size включает завершающий 0 или нет?
PM   Вверх
marsh123
Дата 2.3.2012, 23:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.6.2008

Репутация: нет
Всего: нет



Цитата(bsa @ 2.3.2012,  22:17)
marsh123, у тебя size включает завершающий 0 или нет?

Включает, я добавляю завершающий 0 в конце строки, затем уменьшаю номер  последнего символа, чтобы затирать ноль при след добавлении нормального символа, в итоге расширение size произойдет априори.

Это сообщение отредактировал(а) marsh123 - 2.3.2012, 23:10
PM MAIL   Вверх
bsa
Дата 3.3.2012, 08:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



marsh123, тебе не кажется, что это слишком сложно?
Все делается как-то так:
Код
String()
   : data(0)
   , capacity(0)
   , size(0)
{}

template<size_t N>
String(const char (&array)[N]) 
   : data(0)
   , capacity(0)
   , size(0)
{
   assign(array, array + N);
}

String(const char *text) 
   : data(0)
   , capacity(0)
   , size(0)
{
   assign(text, text + strlen(text) + 1);
}

String(const String &other) 
   : data(0)
   , capacity(0)
   , size(0)
{
   if (other.size > 0)
      assign(other.data, other.data + size + 1);
}

~String() 
{
   delete []data;
}

void assign(const char *first, const char *last)  
{
   delete []data;
   capacity = last - first;
   size = capacity - 1;

   if (last[-1] != '\0') {
      ++capacity;
      ++size;
   }
   data = new char[capacity];
   memcpy(data, first, last - first);
   data[size] = '\0'; //принудительное добавление нуля
}

void swap(String &other)
{
   char *tmp = data;
   data = other.data;
   other.data = tmp;
   size_t t = size;
   size = other.size;
   other.size = t;
   t = capacity;
   capacity = other.capacity;
   other.capacity = t;
}

template<size_t N>
String& operator=(const char (&array)[N]) { String(array).swap(*this); return *this; }
String& operator=(const char *text) { String(text).swap(*this); return *this; }
String& operator=(const String &other) {String(other).swap(*this); return *this; }

Кстати, наиболее оптимально вместо size и capacity использовать указатели на конец данных и конец выделенной области соответственно.
PM   Вверх
marsh123
Дата 3.3.2012, 18:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 69
Регистрация: 22.6.2008

Репутация: нет
Всего: нет



Цитата(bsa @ 3.3.2012,  08:48)
marsh123, тебе не кажется, что это слишком сложно?
Все делается как-то так:
Код
String()
   : data(0)
   , capacity(0)
   , size(0)
{}

template<size_t N>
String(const char (&array)[N]) 
   : data(0)
   , capacity(0)
   , size(0)
{
   assign(array, array + N);
}

String(const char *text) 
   : data(0)
   , capacity(0)
   , size(0)
{
   assign(text, text + strlen(text) + 1);
}

String(const String &other) 
   : data(0)
   , capacity(0)
   , size(0)
{
   if (other.size > 0)
      assign(other.data, other.data + size + 1);
}

~String() 
{
   delete []data;
}

void assign(const char *first, const char *last)  
{
   delete []data;
   capacity = last - first;
   size = capacity - 1;

   if (last[-1] != '\0') {
      ++capacity;
      ++size;
   }
   data = new char[capacity];
   memcpy(data, first, last - first);
   data[size] = '\0'; //принудительное добавление нуля
}

void swap(String &other)
{
   char *tmp = data;
   data = other.data;
   other.data = tmp;
   size_t t = size;
   size = other.size;
   other.size = t;
   t = capacity;
   capacity = other.capacity;
   other.capacity = t;
}

template<size_t N>
String& operator=(const char (&array)[N]) { String(array).swap(*this); return *this; }
String& operator=(const char *text) { String(text).swap(*this); return *this; }
String& operator=(const String &other) {String(other).swap(*this); return *this; }

Кстати, наиболее оптимально вместо size и capacity использовать указатели на конец данных и конец выделенной области соответственно.

Да я не спорю, что более оптимально написать можно, тоже сначала также примерно хотел сократить, но решил уже довести до рабочего состояния.
А что на счет стека? Там вопрос задавал, как сделать, чтобы:
Код

s = "aaa", "bbb", "ccc";

Где s - объект стека, запушило слева направо эти строчки.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.1413 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.