LINUX.ORG.RU

Передача параметров по ссылке. Что не так с моей программой?

 , , , ,


0

1

Есть вот такая программа:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

class no_object : public std::exception
{
protected:
    std::string mMsg;
public:
    no_object(const char* msg) : mMsg(msg) {}
    virtual ~no_object() noexcept override {}
    virtual const char* what() const noexcept override { return mMsg.c_str(); }
};


using namespace std;

class Object {
public:
    Object(const string info) : information(info) { cout << "Object constructor!" << endl; }
    ~Object() { cout << "Object destructor!" << endl; }
    Object(const Object& obj) { information = obj.information; cout << "Copy constructor!" << endl; }
    void setInformation() {  }
    string getInformation() const { return information; }
private:
    string information;
};

class Storage {
public:
    Storage(const size_t width) {
        objs = static_cast<Object*>(malloc(sizeof(Object) * width));

        if (objs == NULL)
            throw std::bad_alloc();

        lastPointer = objs + sizeof (Object) * (width - 1);
    }

    void storeObject(Object& obj, size_t index) {
        if (isIndexOutOfRange(index))
            throw std::out_of_range("Oops, index is out of range!");

        availableIndexes.push_back(index);
        objs[index] = obj;
    }

    Object& getObjectAtIndex(size_t index) const {
        if (isIndexOutOfRange(index))
            throw std::out_of_range("Oops, index is out of range!");

        auto it = find(availableIndexes.begin(), availableIndexes.end(), index);
        if (it == availableIndexes.end())
            throw no_object("Oops, the object for this index is not set!");

        return objs[index];
    }

    ~Storage() {
        free(objs);
    }
private:
    bool isIndexOutOfRange(size_t index) const noexcept {
        Object* indexPointer = objs + sizeof (Object) * index;

        if (indexPointer > lastPointer)
            return true;

        return false;
    }

    vector<size_t> availableIndexes;

    Object* objs;
    Object* lastPointer;
};

int main()
{
    Storage storage(3);
    {
        cout << "1" << endl;
        Object obj = Object("lambo");
        cout << "2" << endl;
        Object& objRef = obj;
        cout << "3" << endl;
        storage.storeObject(objRef, 2);
    }

    cout << "4" << endl;
    Object savedObject = storage.getObjectAtIndex(2);
    cout << "Information from stored object is " << savedObject.getInformation() << endl;

    return 0;
}

Вывод который она выдает:

1
Object constructor!
2
3
Object destructor!
4
Copy constructor!
Information from stored object is lambo
Object destructor!

По ней у меня есть несколько вопросов?

1. Мы в памяти сохранили объект который получили по ссылке перед тем как она вышла из области видимости. При условии что этот объект получаю из памяти, можно ли так делать? Если нет то как это исправить. Только просьба не советовать stl и vector. Я именно хочу разобраться глубоко при работе с памятью.

2. Есть ли какой-то undefined behavior в ее использовании кроме копирования объекта Storage?

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

lastPointer = objs + sizeof (Object) * (width - 1);

У тебя кривая арифметика указателей. При прибавлении к указателю Type* уже учитывается sizeof(Type).

Ivan_qrt ★★★★★ ()

Вот что бывает, когда людям дают ссылки без объяснения указателей.

Есть ли какой-то undefined behavior в ее использовании

И еще какой!

    {
        cout << "1" << endl;
        Object obj = Object("lambo");
        cout << "2" << endl;
        Object& objRef = obj;
        cout << "3" << endl;
        storage.storeObject(objRef, 2);
    }

Создал объект на стеке.

Получил указатель на объект.

Сохранил указатель в массив.

Уничтожил объект.

Попытался воспользоваться объектом по указателю.

Всё нормально, сам как думаешь?

Deleted ()
Ответ на: комментарий от Deleted

Ну во первых я сохранил сам объект, а не указатель. И насколько я понимаю он у нас существует в памяти. Честно не вижу ничего криминального.

А во вторых я сомневаюсь и потому спросил.

ECLIPSE ()
Последнее исправление: ECLIPSE (всего исправлений: 1)
Ответ на: комментарий от ECLIPSE

Перечитал код еще раз. Таки да. Извиняюсь за невнимательность.

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

Deleted ()
Ответ на: комментарий от Deleted

Можешь плиз написать пример? Не совсем понимаю что ты имеешь ввиду. И я думаю многим на лоре хотелось бы разобраться со всеми тонкостями при работе со ссылками тоже. Я вот пока что начал копать в сторону с placement-new.

ECLIPSE ()

Ахтунг:

objs = static_cast<Object*>(malloc(sizeof(Object) * width));

Выделяем память под хранение width объектов Object, но инициализировать их, вызывая конструктор, мы не будем.

objs[index] = obj

Вызываем операцию присванивания (то бишь логически это objs[index].operator=(obj)), будто у нас objs[index] корректно проинициализирован конструктором, и можем дергать ему методы.

Не делай так на крестах. Для этого есть си. И никогда не выделяй в крестах память malloc()-ом.

Deleted ()

Мы в памяти сохранили объект который получили по ссылке перед тем как она вышла из области видимости. При условии что этот объект получаю из памяти, можно ли так делать?

Ты его скопировал к себе в storage. Поэтому можно. После того, как ты его скопировал, не важно, что стало с первоначальным объектом.

Не совсем понятно, что именно ты подразумевал, лучше проясни вопрос.

Есть ли какой-то undefined behavior в ее использовании кроме копирования объекта Storage?

Я не особо спец по ub, но вроде нормально всё.

Правда если у тебя получение/сохранение происходит по индексам, то нет особого смысла хранить указатели indexPointer и lastPointer. Храни просто size_t capacity и size_t size и index сравнивай с ними.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от Deleted

Практика, безусловно, плохая, но у ТС при этом, вроде как, всё корректно.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от Deleted

Так в этом то и основная идея: избежать использования создания объектов для всего массива. Как мне тогда использовать эту же идею только исправить чтобы все было корректно? Использовать С каким-то образом?

Я так понял что ты имеешь ввиду что нельзя использовать оператор присваивания? А почему?

ECLIPSE ()
Последнее исправление: ECLIPSE (всего исправлений: 1)
Ответ на: комментарий от ECLIPSE

Я вброшу свои 5 копеек: devzero тебе писал, что совсем небезопасно использовать malloc в плюсах при работе с пользовательскими типами данных (читай классами).

А если и использовать, нужно делать чуть больше телодвижений чем в «плюсовом» варианте с new/delete.

Новичку будет проще ошибаться :-)

Раз

Два

Twissel ★★★★★ ()
Ответ на: комментарий от ECLIPSE

Так в этом то и основная идея: избежать использования создания объектов для всего массива

Я правильно понял, что ты хочешь быть кошерным и избежать malloc/free, при этом не вызывая default constructor создавая таблицу objs? Если понял правильно, то в C++ можно сделать так (если пернул в лужу и задача другая, сорри):

objs = static_cast<Object*>(malloc(sizeof(Object) * width));

objs = operator new(sizeof(Object)*width)

Это семантически тоже самое, чтo:

objs = static_cast<Object*>(malloc(sizeof(Object) * width));

operator new(size_t) возвращает continuos memory без вызова конструкторов.

objs[index] = obj;

new (objs+value) Object(obj);
new (&objs[value]) Object(obj); // или так, same shit

Так мы вместо оператора присваивания используем копирующий конструктор, что может быть эффективней (так как ему не надо, например, создавать копию Object чтобы быть exception-safe, тут также может быть вызван move constructor если он есть). Оператор placement new позваляет указать где создать объект.

Обрати внимание, что:

Object* objs = operator new[N];
это функция «operator new», делающая тоже самое, что в C malloc().

а вот

new (p) T();

Это placement operator new, вызывающий конструктор T, но создающи объект в месте указанном указателем p. Т.е. это разные вещи.

Надо помнить, что при использовании placement new нельзя делать

delete objs[value]:

илил

delete[] objs;

Надо руками вызвать деструктор, а потом вызвать функцию «operator delete» (которая по аналогии с функцией «operator new», делает тоже, что free() в C).

// construct() constructs a new object in 
// a given location using an initial value
//
template <class T1, class T2>
void construct( T1* p, const T2& value )
{
  new (p) T1(value);
}

The above form of new is called «placement new,» and instead of allocating memory for the new object, it just puts it into the memory pointed at by p. Any object new'd in this way should generally be destroyed by calling its destructor explicitly (as in the following two functions), rather than by using delete.

// destroy() destroys an object or a range 
// of objects
//
template <class T>
void destroy( T* p )
{
  p->~T();
}

template <class FwdIter>
void destroy( FwdIter first, FwdIter last )
{
  while( first != last )
  {
    destroy( &*first );
    ++first;
  }
}

template <class T> 
StackImpl<T>::~StackImpl()
{
    destroy( v_, v_+vused_ ); // this can't throw
    operator delete( v_ );
}

(c) Herb Sutter «Exceptional C++», item 12 solution

Т.е. каждый элемент obj надо уничтожать так:

objs[i]->~Object();
operator delete(objs+i); // тоже что и free()
operator delete(&objs[i]); // или так, что тоже самое

dissident ★☆ ()
Последнее исправление: dissident (всего исправлений: 4)
Ответ на: комментарий от dissident

Т.е. каждый элемент obj надо уничтожать так:

Поскольку функция «operator delete» не вызывает деструктора, то нету функции «operator delete[]» и в твоем случае, поскольку ты запрашиваешь память для всей таблицы obj одним вызовом функции «operator new», то не надо каждый элемент отдельно удалять функций «operator delete». Так что этот пример:

objs[i]->~Object();
operator delete(objs+i); // тоже что и free()
operator delete(&objs[i]); // или так, что тоже самое

у меня вышел неправильно. Вот более правильные примеры:

Object *objs = operator new(sizeof(Object)*width);
// Do smth with objs
for (int i = 0; i < width; i++) {
   objs[i].~Object();
}
operator delete(objs);

В случае одного объекта:

Object *obj = operator new(sizeof(Object));
// Do smth with obj
obj->~Object();
operator delete(obj);
[/cpde]

dissident ★☆ ()
Последнее исправление: dissident (всего исправлений: 5)

objs + sizeof (Object) * (width - 1)
objs[index]

objs не является указателем на элемент массива. Поэтому применять к нему арифметику указателей — UB.

anonymous ()
Ответ на: комментарий от dissident

objs[i]

Та же самая ошибка с арифметикой.

anonymous ()
Ответ на: комментарий от anonymous

objs не является указателем на элемент массива. Поэтому применять к нему арифметику указателей — UB.

С чего это? Нет там никакого ub. Арифметика неправильная, но не более.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от Ivan_qrt

С чего это?

Ты читать умеешь? «objs не является указателем на элемент массива.»

Нет там никакого ub.

Ты скозал?

anonymous ()
Ответ на: комментарий от anonymous

Ну и что? operator new(size_t) или же malloc(size_t) возвращает указатель на continuous memory. Если его скастить на T*, но если T определено, то t + N (где t типа T а N size_t) «перенесет» указатель вперед на N * sizeof(T)

The call to malloc allocates an array of whatever size you desire, and the pointer points to that array's first element. You can either index through the array pointed to by p using normal array indexing, or you can do it using pointer arithmetic. C sees both forms as equivalent.

(c) https://computer.howstuffworks.com/c31.htm

Т.е. objs здесь ЯВЛЯЕТСЯ указателем на элемент массива. Другое дело что надо было не «+ sizeof(Object) * (width - 1)» а «+ (width - 1)» и все было бы хорошо.

dissident ★☆ ()
Ответ на: комментарий от anonymous

Ты читать умеешь? «objs не является указателем на элемент массива.»

Ну и что? objs указывает на continuos memory, где один за другим сидят объекты Object. Alignment может произойти внутри объекта Object, а не между ними.

Насколько я понял главная ошибка автора была тут:

lastPointer = objs + sizeof (Object) * (width - 1);

тогда как должно быть

lastPointer = objs + (width - 1).

По поводу оператора присваивания я не помню генерируется ли он автоматом, если копирующий конструктор определен автором, если нет, то проблема была в его отсутсвии в конструкции

objs[i] = obj;

Я показал, как это обойти используя placement new вместо оператора присваивания. Но даже если оставить как есть, то вроде должно работать. Rule of three говорит только, что следовало бы оределить implicit copy assignment если copy constructor explicit:

The rule of three (also known as the Law of The Big Three or The Big Three) is a rule of thumb in C++ (prior to C++11) that claims that if a class defines one (or more) of the following it should probably explicitly define all three:

(c) https://en.wikipedia.org/wiki/Rule_of_three_(C++_programming)

Впрочем это легко проверить:

Как и следовало ожидать наличие explicit copy costructor не мешает генерации implicit copy assignment operator.

#include <iostream>
using namespace std;

struct C
{
    C() { cout << "default ctor" << endl; val = 42; }
    C(int val) { cout << "default ctor" << endl; this->val = val; }
    C(const C& other) { cout << "copy ctor" << endl; val = other.val; }

    int val;
};

int main(int argc, char *argv[])
{
    C c;
    C c2(666);
    c2 = c;
    cout << "c2.val should be 42 now because of auto-generated copy assignment operator" << endl;
    cout << "and it is: " << c2.val << endl;
}
[ss@arch tmp]$ ./a.out 
default ctor
default ctor
c2.val should be 42 now because of auto-generated copy assignment operator
and it is: 42

Т.е. код автора работал бы и с «objs = obj» после исправления ошибки в pointer arithmetics.

dissident ★☆ ()
Ответ на: комментарий от dissident

objs указывает на continuos memory

Как будто это имеет какое-то значение. http://eel.is/c draft/expr.add#4 вот условия, при которых можно прибавлять (вычитать) к выражению-указателю выражение-число.

Опуская случай когда к нулевому указателю можно прибавлять 0, остаёмся с пунктом, что указатель должен указывать на элемент массива. Чего у нас нет.

Жду видео с поеданием кепки.

anonymous ()
Ответ на: комментарий от anonymous

Та же самая ошибка с арифметикой.

Где? objs типа Object*. А значит

objs[i]
это тоже самое, что
objs + i
. Память на которую указывать objs содержит последовательно элементы типа Object. objs указывает на первый. А значит
objs[i]
или
objs+i
соответсвенно на i'тый.

Конечно, есть возможность, что я пернул в лужу, но прошу обосновать. Тогда съем свою кепку.

dissident ★☆ ()
Ответ на: комментарий от anonymous

If P points to element x of an array object x with n elements

Ну так он и указывает.

Иначе вообще в C невозможно было бы динамических таблиц созданных с помощью malloc индексировать.

Что такое array? Чем отличается array[] от array*?

И еще в догонку: чем отличается array[2] от 2[array]?

dissident ★☆ ()
Последнее исправление: dissident (всего исправлений: 1)
Ответ на: комментарий от dissident

Ну так он и указывает.

Дэ? А откуда там «an array object x with n elements» взялся?

Иначе вообще в C невозможно было бы динамических таблиц созданных с помощью malloc индексировать.

То, что оно работает это не значит, что оно корректно определено, а только значит, что оптимизаторы не портят наивной картины мира. В C модель памяти неоднозначная и дырявая как дуршлаг.

Ну и вообще тут C++-тред, а не C-тред.

anonymous ()
Ответ на: комментарий от dissident

И еще в догонку: чем отличается array[2] от 2[array]?

Этот «трюк» — твой потолок в C/C++? Слабовато.

anonymous ()
Ответ на: комментарий от anonymous

Дэ? А откуда там «an array object x with n elements» взялся?

Еще раз повторю вопросы:

  • Что такое array?
  • Чем отличается array[2] от 2[array]?
  • Что значит, что в array N элементов?

В C/C++ (не учитывая std::array) вообще нет такой вещи как array. Вернее есть, но эта вещь ничем не отличается от указателя на continuos memory.

То, что оно работает это не значит, что оно корректно определено, а только значит, что оптимизаторы не портят наивной картины мира. В C модель памяти неоднозначная и дырявая как дуршлаг.

Не понимаю, что ты хочешь этим сказать. Можно примеры?

Ну и вообще тут C++-тред, а не C-тред.

А в C++ нету C-like arrays?

dissident ★☆ ()
Последнее исправление: dissident (всего исправлений: 1)
Ответ на: комментарий от anonymous

Этот «трюк» — твой потолок в C/C++? Слабовато.

Извини меня зае...о. Пойду лучше чем-нибудь осмысленным займусь вместо кормежки троля. Если я пернул в лужу (я человек, а не робот и совершаю ошибки) прошу показать где (с примерами).

dissident ★☆ ()
Ответ на: комментарий от dissident

Еще раз повторю вопросы:

А толку? Они тут не в тему.

В C/C++ (не учитывая std::array) вообще нет такой вещи как array. Вернее есть, но эта вещь ничем не отличается от указателя на continuos memory.

Откуда ты этот бред берёшь?

Если я пернул в лужу (я человек, а не робот и совершаю ошибки) прошу показать где (с примерами).

http://eel.is/c draft/intro.object#def:object «An object is created by a definition ([basic.def]), by a new-expression, when implicitly changing the active member of a union ([class.union]), or when a temporary object is created ([conv.rval], [class.temporary]).»

Соответственно, чтобы создать «an array object x with n elements» нужно выполнить что-то из вышеперечисленного.

int arr[2]; // an array object with 2 elemens is created
new int[3]; // an array object with 3 elements is created
malloc(sizeof(int)*3); // no array object is created here

Так что указателя на array element не может быть просто потому, что никакого array object ты не создавал.

Это же элементарно.

anonymous ()
Ответ на: комментарий от anonymous

by a new-expression

Прицепился к malloc'у. Ну хорошо, не malloc, так new. А это что не array object?

int* array = operator new(sizeof(int)*N);

? Если даже by definition нет, то «класть» туда int'ы и обращаться к ним по

array[i]
вполне себе можно.

Если нет, то Herb Sutter в примере из exceptional C++ (item 12, 13) явно не знает C++:

template <class T> class StackImpl 
{
/*????*/:
  StackImpl(size_t size=0);
  ~StackImpl();
  void Swap(StackImpl& other) throw();

  T*     v_;      // ptr to a memory area big
  size_t vsize_;  //  enough for 'vsize_' T's
  size_t vused_;  // # of T's actually in use

private:
  // private and undefined: no copying allowed
  StackImpl( const StackImpl& );
  StackImpl& operator=( const StackImpl& );
};

template <class T> 
StackImpl<T>::StackImpl( size_t size )
  : v_( static_cast<T*>
          ( size == 0
            ? 0
            : operator new(sizeof(T)*size) ) ),
    vsize_(size),
    vused_(0)
{
}

template <class T> 
class Stack : private StackImpl<T>
{
public:
  Stack(size_t size=0)
    : StackImpl<T>(size)
  {
  }

// construct() constructs a new object in 
// a given location using an initial value
//
template <class T1, class T2>
void construct( T1* p, const T2& value )
{
  new (p) T1(value);
}

Stack(const Stack& other) 
  : StackImpl<T>(other.vused_)
{
  while( vused_ < other.vused_ )
  {
    construct( v_+vused_, other.v_[vused_] );
    ++vused_;
  }
}

T& Top() 
{
  if( vused_ == 0 )
  {
    throw "empty stack";
  }
  return v_[vused_-1];
}

Посмотри на конструкцию

v_+vused
в Stack copy constructor. Посмотри на конструкцию
return v_[vused_-1]
в Stack::Top.

dissident ★☆ ()
Последнее исправление: dissident (всего исправлений: 4)
Ответ на: комментарий от dissident

А это что не array object?

Ты не отличаешь operator new от new-expression?

Если нет, то Herb Sutter в примере из exceptional C++ (item 12, 13) явно не знает C++

Вполне может знать, что там UB. То, что этот код в книге Саттера не доказывает, что в нём нет UB.

anonymous ()
Ответ на: комментарий от dissident

До C++11 вообще не было способа написать свой аналог std::vector (с постепенным конструированием объектов в заранее с запасом выделенной памяти) без UB.

anonymous ()
Ответ на: комментарий от anonymous

Ты не отличаешь operator new от new-expression?

new expression вызывает конструкторы.

Но мы же вроде не об этом спорим? Ты сказал, что если что-то создано при помощи оператора new то:

objs[i]

это «та же самая ошибка с арифметикой».

Я тебе привожу примеры с malloc, что можно сделать;

int* a = malloc(size(a)*3);
a[1] = 2;

и что это никакое не UB, потому что это тоже самое, что

int *p = a + 1;
*p = 2;

Тоже самое будет работать с operator new.

Я очень многого не знаю о C++, но из всего этого долгого общения, так и не увидел конкретного примера UB.

До C++11 вообще не было способа написать свой аналог std::vector (с постепенным конструированием объектов в заранее с запасом выделенной памяти) без UB.

Так же как и тут не вижу.

Вполне может быть что placement new имеет какие-то ограничения о которых я не знаю. Но pointer arithmetic то почему не будет работать?

Может иначе. В какой строчке здесь UB и почему?

int *a = (int*)malloc(sizeof(int)*2);
a[0] = 4;
a[1] = 2
printf("%d%d\n", a[0], a[1]);

А тут?

std::string *a = (std::string*)operator new(sizeof(std::string)*2);
new (&a[0]) std::string("4");
new (&a[1]) std::string("2");
cout << a[0] << a[1] << std::endl;

PS Это не holly war, мне действительно интересно.

dissident ★☆ ()
Последнее исправление: dissident (всего исправлений: 7)
Ответ на: комментарий от dissident

Кроме того, я даже не знаю где тут UB:

std::string *a = (std::string*)operator new(sizeof(std::string)*2);
a[0] = "4";
a[1] = "2";
cout << a[0] << a[1] << std::endl;

PS Если вы не можете объяснить это просто - значит, вы сами не понимаете этого до конца. (с)

dissident ★☆ ()
Ответ на: комментарий от dissident

Ты не отличаешь operator new от new-expression?

new expression вызывает конструкторы.

Это так в какой-нибудь книжке про «как пройти собеседование на C++-джуниора» дрессируют отвечать на вопрос про отличие malloc/operator new от new-expression?

В формальной объектной модели C++ new-expression — один из способов создания объекта.

Ты сказал, что если что-то создано при помощи new-expression, то: objs это «та же самая ошибка с арифметикой».

Я такого не говорил.

Так же как и тут не вижу.

Чего не видишь? Способа? В C++11 завезли aligned_storage. В C++17 разрешили размещать объекты внутри символьных массивов не уничтожая эти массивы.

Но pointer arithmetic то почему не будет работать?

Я уже показал, почему.

Я тебе привожу примеры с malloc, что можно сделать;

int* a = malloc(size(a)*3);
a[1] = 2;
и что это никакое не UB

От того, что ты повторишь ещё N раз, это UB быть не перестанет.

В какой строчке здесь UB и почему?

Если говорить совсем откровенно, то во всех трёх UB в первой строке. Ни malloc, ни operator new не вписываются в http://eel.is/c draft/basic.compound#3.sentence-8 этот список значений указателя, если возвращают ненулевой указатель.

anonymous ()
Ответ на: комментарий от dissident

Если вы не можете объяснить это просто - значит, вы сами не понимаете этого до конца.

Ванильные цитатки — это, конечно, аргумент.

Я объяснил очень просто. Дал ссылки на стандарт. Но ты даже new-expression от operator new отличить не можешь.

anonymous ()
Ответ на: комментарий от anonymous

Ты читать умеешь? «objs не является указателем на элемент массива.»

Анон, ты глуп. objs является указателем на элемент массива.

Ты скозал?

Ну я конечно, там даже подписано специально.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от Ivan_qrt

Анон, ты глуп.

Со второго сообщения ты перешёл к оскорблениям. Слабовато у тебя с аргументами.

objs является указателем на элемент массива.

Какого массива?

anonymous ()
Ответ на: комментарий от anonymous

Ну ок.

Кепку съем, как найду, куда я ее дел.

DANGER: You are taking sole responsibility that the pointer you pass to the «placement new» operator points to a region of memory that is big enough and is properly aligned for the object type that you're creating. Neither the compiler nor the run-time system make any attempt to check whether you did this right. If your Fred class needs to be aligned on a 4 byte boundary but you supplied a location that isn't properly aligned, you can have a serious disaster on your hands (if you don't know what «alignment» means, please don't use the placement new syntax). You have been warned.

(c) http://www.cs.technion.ac.il/users/yechiel/c -faq/placement-new.html

С другой стороны правильно ли я понимаю, что если sizeof(T) < __STDCPP_DEFAULT_NEW_ALIGNMENT__ то тем не менее все может работать (и поэтому работает с int'ами например)?

dissident ★☆ ()
Ответ на: комментарий от dissident

И кроме того почему alignment должен не совпадать? Я же не делаю так:

int* a = static_cast<int>(operator new(sizeof(int)*N));
new (a+1) int(42)
dissident ★☆ ()
Последнее исправление: dissident (всего исправлений: 1)
Ответ на: комментарий от dissident

Разве что 'a' начинается не под адресом делимым на aligment(int) (тогда и a+1 не нужно)...

Но 'a' же не часть struct'а, просто автоматическая переменная.

В общем ты не оставил мне другого выбора, как понять все что нужно про alignment и alignment + placement new, за что спасибо.

С другой стороны эти «слабовато» (это был пример того что array[2] = array + 2); «джуниор» (я же выше признался что многого не знаю о C++"); бред про pointer arithmetic (даже если он при памяти «в которой» создается объект при помощи placement new и которая не выровнена работает не корректно, то проблема не в pointer arithmetics, а в alignment); какие-то там еще подъ...ки и махание перед носом пальцами - за это не спасибо. Трата драгоценного времени.

Хотя я забыл, это же ЛОР...

dissident ★☆ ()
Последнее исправление: dissident (всего исправлений: 3)
Ответ на: комментарий от anonymous

То, что этот код в книге Саттера не доказывает, что в нём нет UB.

Кстати код в книге Саттера имеет T* v_ как первое поле в StackImpl.

Не означает ли это что alignment v_ при

v_ = operator new (sizeof(T)*size)
не должен приводить к undefined behaviour так как StackImpl::v_ выровнена по крайней мере по границе __STDCPP_DEFAULT_NEW_ALIGNMENT__?

PS Нет, я еще не понял всего про alignment vs operator new, поэтому просто вежливо спрашиваю. Если я правильно понял, то если тот, кто пишет код, обеспечит то что память будет aligned под то, что там хранится, то UB не будет.

PPS Зачем тогда вообще в C++98 placement new если нету alignof? Значит способ обеспечить alignment у operator new(size_t) без alignof есть (был?)

dissident ★☆ ()
Последнее исправление: dissident (всего исправлений: 3)
Ответ на: комментарий от dissident

Ты вообще необучаем, оказывается.

anonymous ()
Ответ на: комментарий от anonymous

Из того же Саттера:

Alignment. Any memory that's allocated dynamically via new or malloc is guaranteed to be properly aligned for objects of any type, but buffers that are not allocated dynamically have no such guarantee:

char* buf1 = (char*)malloc( sizeof(Y) ); 
char* buf2 = new char[ sizeof(Y) ];
char  buf3[ sizeof(Y) ];
new (buf1) Y;     // OK, buf1 allocated dynamically (A)
new (buf2) Y;     // OK, buf2 allocated dynamically (B)
new (&buf3[0]) Y; // error, buf3 may not be suitably aligned
(reinterpret_cast<Y*>(buf1))->~Y(); // OK
(reinterpret_cast<Y*>(buf2))->~Y(); // OK
(reinterpret_cast<Y*>(&buf3[0]))->~Y(); // error

(c) тот же Herb Sutter «Exceptional C++» Item 30 solution.

Placement new есть в C++98. alignof нету. Значит есть способ, заставить его работать без undefined behavior.

Я исхожу из того, что разница между тем что возвращает

new int[N]

и

static_cast<int*>(operator new(sizeof(int)*N))

не должна отличаться ничем, кроме как тем, что там в находится сразу после создания: мусор или нули или DEADBEAF или какое-то иное значение по умолчанию или unspecified whatever.

sizeof(int)
уже должен учитывать alignment, поэтому размер обоих массивов должен быть такой же. Разве что в массиве между элементами есть «дырки» из-за того же alignment, но насколько я понимаю это не так? Если же это именно так, то почему он об этом вовсе не вспомнил в предыдущем примере со StackImpl?

Выше Саттер жалуется что для буферов на stack'e это не работает, а на heap'e работает, я не знаю еще почему, ты знаешь? Линки про «объекты» из стандарта, возможно и исчерпывающе отвечают на вопрос, но не мне (необучаем же). Кроме того еще и водку пью. Но это кого-то е...т, а?

Какова информационная ценность обсуждения моих недостатков? Лучше бы не поленился, а объяснил для дебилов вроде меня доступнее. Может еще кому-то пригодится.

dissident ★☆ ()
Последнее исправление: dissident (всего исправлений: 2)
Ответ на: комментарий от dissident

Лучше бы не поленился, а объяснил для дебилов вроде меня доступнее.

Да куда уж доступнее?

Для арифметики с указателем нужно, чтобы указатель указывал на элемент массива. Иначе UB. Массив (или другой какой объект) создаётся так-то или так-то. Массив не создавался? Значит имеем UB.

А отсылки к Саттеру смешны. Его книги это не стандарт C++.

anonymous ()
Ответ на: комментарий от anonymous

Я все равно не убежден. Чем отличается, вернее что такого «массивного» в

new int[N]
, а что такого «не массивного» в
static_cast<int*>(operator new(sizeof(int)*N))
кроме инициализации int'ов? Почему арифметика не будет работать? Там что таки «дырки» между int'ами? Стандарт стандартом, но у всего же должно быть объяснение.

PS И что в ANSI C тоже? Т.е. в ANSI C так нельзя?

int *a = (int*) malloc(sizeof(int)*N);

for (int i = 0; i < N; i++)
{
   a[i] = i;
}

for (int i = 0; i < N; i++)
{
   printf("%d\n", a[i] = i);
}

A lyzka na to «niemozliwe» (c) https://www.youtube.com/watch?v=INBOvCWYqlU

А отсылки к Саттеру смешны. Его книги это не стандарт C++.

Ну он же вроде в каком-то там комитете заседает, который этот стандарт выдумывает, разве нет?

dissident ★☆ ()
Ответ на: комментарий от dissident

Я все равно не убежден.

Так это твои проблемы.

Чем отличается

Выше была цитата из стандарта.

И что в ANSI C тоже? Т.е. в ANSI C так нельзя?

Про C я тоже уже отвечал.

Ну он же вроде в каком-то там комитете заседает, который этот стандарт выдумывает, разве нет?

От этого его книги не становятся стандартом, лол!

anonymous ()
Ответ на: комментарий от dissident

PS Если даже допустим new T[] что-то там более иначе делает (например где-то внутри куска памяти в *(a-2), например, хранит количество элементов, чтобы корректно вызвать деструкторы при delete[] то нам это не болит, потому что мы руками вызываем деструкторы а потом делаем operator delete(a), который не полезет в *(a-1) например для уточнения размера всего a.

dissident ★☆ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.