Realloc () / Изменение размера объекта в C ++ для реализации строки - PullRequest
0 голосов
/ 16 марта 2009

Когда они представлены в памяти, объекты C ++ совпадают со структурами C?

Например, с C я мог бы сделать что-то вроде этого:

struct myObj {
       int myInt;
       char myVarChar;
};

int main() {
       myObj * testObj = (myObj *) malloc(sizeof(int)+5);
       testObj->myInt = 3;
       strcpy((char*)&testObj->myVarChar, "test");
       printf("String: %s", (char *) &testObj->myVarChar);
}

Я не думаю, что C ++ позволяет перегрузить оператор + для встроенного типа char *.

Итак, я хотел бы создать свой собственный легкий класс строк, который не имеет дополнительных издержек, которые есть у std::string. Я думаю, что std::string представляется смежно:

(int)length, (char[])data

Мне нужна точно такая же функциональность, но без префикса длины (экономия 8 байтов).

Вот код, который я использую для тестирования, но это приводит к segfault

#include <iostream>
using namespace std;
class pString {
    public:
        char c;
        pString * pString::operator=(const char *);
};


pString * pString::operator=(const char * buff) {

    cout << "Address of this: " << (uint32_t) this << endl;
    cout << "Address of this->c: " << (uint32_t) &this->c << endl;

    realloc(this, strlen(buff)+1);
    memcpy(this, buff,  strlen(buff));
    *(this+strlen(buff)) = '\0';

    return this;
};

struct myObj {
        int myInt;
        char myVarChar;
};

int main() {

    pString * myString = (pString *) malloc(sizeof(pString));
    *myString = "testing";
    cout << "'" << (char *) myString << "'";    
}

Редактировать: никто действительно не понимает, что я хочу сделать. Да, я знаю, что могу иметь указатель на строку в классе, но это на 8 байт дороже, чем простая строка, я хотел точно такое же внутреннее представление. Спасибо за попытку, хотя


Редактировать: конечным результатом того, чего я хотел достичь, была возможность использовать оператор + без использования дополнительной памяти по сравнению с использованием strcat и т. Д.

const char * operator+(const char * first, const char * second);

Ответы [ 17 ]

1 голос
/ 16 марта 2009

Если вы хотите что-то, что в основном совпадает с std::string, за исключением того, что оно не знает, какова длина строки, вы должны узнать, как работает std::string, какой оператор перегружен и т. Д., А затем подражать этому , только с теми различиями, которые вы хотите.

Однако вряд ли в этом есть какая-то реальная точка зрения.

Что касается вашего последнего обновления - вы говорите, что хотите дизайн, в котором общий код приложения будет передавать голые указатели на объекты кучи. Без автоматической очистки.

Это очень плохая идея.

1 голос
/ 17 марта 2009
#include <iostream>
using namespace std;
class pString {
public:
    char c;
    pString * pString::operator=(const char *);
};

pString * pString::operator=(const char * buff) {

    cout << "Address of this: " << (uint32_t) this << endl;
    cout << "Address of this->c: " << (uint32_t) &this->c << endl;

    char *newPoint = (char *)realloc(this, strlen(buff)+1);
    memcpy(newPoint, buff,  strlen(buff));
    *((char*)newPoint+strlen(buff)) = '\0';

    cout << "Address of this After: " << (uint32_t) newPoint << endl;

    return (pString*)newPoint;
};

int main() {

    pString * myString = (pString *) malloc(sizeof(pString));
    *myString = "testing";

    cout << "Address of myString: " << (uint32_t) myString << endl;

    cout << "'" << (char *) myString << "'";    
}

Работает, когда realloc не переназначает указатель, т.е.

Адрес этого: 1049008 Адрес этого-> c: 1049008 Адрес этого после: 1049008 Адрес myString: 1049008 «тестирование»

Работает, но когда происходит следующее, это терпит неудачу

Адрес этого: 1049008 Адрес этого-> c: 1049008 Адрес этого после: 1049024 Адрес myString: 1049008 ''

очевидное решение -

this = (pString*) newPoint;

Но компилятор жалуется на недопустимое значение lvalue в присваивании. Кто-нибудь правильный способ обновить это (просто для полноты, я сомневаюсь, что я буду использовать код, поскольку все, кажется, ненавидят его). Спасибо

1 голос
/ 17 марта 2009

Вы не можете перераспределять объекты C ++. Как отметили другие, this на самом деле не указатель, который вы можете изменить, нет гарантии, что он будет указывать на область, к которой у realloc есть доступ.

Одним из решений конкатенации является реализация иерархии классов, которая будет откладывать реальную конкатенацию до тех пор, пока она не потребуется.

Как то так

class MyConcatString;
class MyString {
public:
  MyString(const MyConcatString& c) {
    reserve(c.l.length()+c.r.lenght());
    operator = (l);
    operator += (r);   
  }
  MyConcatString operator + (const MyString& r) const {
    return MyConcatString(*this, r);
  }
};

class MyConcatString {
public:
  friend class MyString;
  MyConcatString(const MyString& l, const MyString& r):l(l), r(r) {};
  ...
  operator MyString () {
    MyString tmp;
    tmp.reserve(l.length()+r.length());
    tmp = l;
    tmp += r;
    return tmp;
  }
private:
  MyString& l;
  MyString& r;
}

Так что, если у вас есть

MyString a = "hello";
MyString b = " world";
MyString c = a + b;

превратится в MyString c = MyConcatString (a, b);

Подробнее см. В разделе «Язык программирования C ++».

Другое решение - заключить char * в структуру, но вам, похоже, не нравится эта идея.

Но какое бы решение вы ни выбрали, объекты в C ++ не могут быть перемещены.

1 голос
/ 16 марта 2009
 #include <iostream>
    using namespace std;
    class pString {
        public:
            char c[1];
            pString * pString::operator=(const char *);
    };


    pString * pString::operator=(const char * buff) {

        cout << "Address of this: " << (uint32_t) this << endl;
        cout << "Address of this->c: " << (uint32_t) &this->c << endl;

        realloc(this->c, strlen(buff)+1);
        memcpy(this->c, buff,  strlen(buff));
        *(this->c+strlen(buff)) = '\0';

        return this;
    };

    struct myObj {
            int myInt;
            char myVarChar;
    };

    int main() {

        pString * myString = (pString *) malloc(sizeof(pString));
        *myString = "testing vijay";
        cout << "'" << ((char*)myString << "'";
    }


This should work. But its not advisable.
1 голос
/ 17 марта 2009

То, что вы хотите сделать, не работает и не может работать в C ++. То, что вы ищете, это C99-функция гибких массивов. Это хорошо работает в C99 по двум причинам: во-первых, у вас нет встроенных конструкторов, а во-вторых, у вас нет наследования (по крайней мере, не как языковая функция). Если класс наследует от другого, то память, используемая подклассом, упаковывается hind в память родительского класса, но в конце структура / класс должна иметь гибкий массив.

class pString {
    char txt[];
}

class otherString : pString { // This cannot work because now the
    size_t len;               // the flexible array is not at the
}                             // end

Возьмем std :: string, который был написан экспертами C ++, я уверен, что они не оставили "хороший трюк" без причины. Если вы по-прежнему обнаруживаете, что они не очень хорошо работают в вашей программе, вместо этого используйте простые строки C, конечно, они не предоставляют приятного API, как вам нужно.

0 голосов
/ 17 марта 2009

Этот код беспорядок, и RnR и другие предложенные не рекомендуется. Но это работает для того, что я хочу сделать:

#include <iostream>
using namespace std;

struct pString {
        /* No Member Variables, the data is the object */ 
        /* This class cannot be extended & will destroy a vtable */
    public:
        pString * pString::operator=(const char *);
};

pString& operator+(pString& first, const char *sec) {


        int lenFirst;
        int lenSec = strlen(sec);
        void * newBuff = NULL;

        if (&first == NULL)
        {
            cout << "NULL" << endl;
            lenFirst = 0; 
            newBuff = malloc(sizeof(pString)+lenFirst+lenSec+1);
        } else {
            lenFirst = strlen((char*)&first);
            newBuff= (pString*)realloc(&first, lenFirst+lenSec+1);
        }

        if (newBuff == NULL)
        {
            cout << "Realloc Failed"<< endl;
            free(&first);
            exit(0);
        }       

        memcpy((char*)newBuff+lenFirst, sec, lenSec);
        *((char*)newBuff+lenFirst+lenSec) = '\0';


        cout << "newBuff: " << (char*)newBuff << endl;

        return *(pString*)newBuff;

};


pString * pString::operator=(const char * buff) {

    cout << "Address of this: " << (uint32_t) this << endl;

    char *newPoint = (char *)realloc(this, strlen(buff)+200);
    memcpy(newPoint, buff,  strlen(buff));
    *((char*)newPoint+strlen(buff)) = '\0';

    cout << "Address of this After: " << (uint32_t) newPoint << endl;

    return (pString*)newPoint;
};


int main() {

    /* This doesn't work that well, there is something going wrong here, but it's just a proof of concept */

    cout << "Sizeof: " << sizeof(pString) << endl;

    pString * myString = NULL;

    //myString = (pString*)malloc(1);
    myString = *myString = "testing";
    pString& ref = *myString;


    //cout << "Address of myString: " << myString << endl;

    ref = ref + "test";
    ref = ref + "sortofworks" + "another" + "anothers";


    printf("FinalString:'%s'", myString);

}
0 голосов
/ 16 марта 2009

Если вам нужна производительность, вы можете написать свой класс следующим образом:

template<int max_size> class MyString
{
public:
   size_t size;
   char contents[max_size];

public:
   MyString(const char* data);
};

Инициализировать max_size соответствующим значением в контексте. Таким образом, объект может быть создан в стеке без выделения памяти.

Можно создать желаемое, перегрузив новый оператор:

class pstring
{
public:
    int myInt;
    char myVarchar;

    void* operator new(size_t size, const char* p);
    void operator delete(void* p); 
};

void* pstring::operator new(size_t size, const char* p)
{
    assert(sizeof(pstring)==size);
    char* pm = (char*)malloc(sizeof(int) + strlen(p) +1 );
    strcpy(sizeof(int)+pm, p);
    *(int*)(pm) = strlen(p);  /* assign myInt */
    return pm;
}

void pstring::operator delete(void* p)
{
    ::free(p);
}


pstring* ps = new("test")pstring;

delete ps;
...