2014-06-03 2 views
0

Я смущен тем, как получить несколько массивов с плавающей точкой из класса с использованием возвращаемых параметров. Вот упрощенный пример.Как правильно вернуть массив элементов класса в качестве возвращаемого параметра

class Points 
{ 
    private: 
     float *x; 
     float *y; 
     float *z; 
    public: 
     void getCoordinates (float *retx, float *rety, float *retz); 
} 

void Points:: getCoordinates (float *retx, float *rety, float *retz) 
{ 
    retx=x; 
    rety=y; 
    retz=z; 
} 

Когда я выполняю назначение внутри функции, значения указателя совпадают. Однако значения меняются, как только я покидаю функцию. Ограничен ли объем указателя?

Основано на How to properly return an array (class member) in C++?, я не думаю, что так должно быть. Дискуссия предполагает, что не только можно передать указатель, но что изменения в памяти с помощью указателя будут влиять на исходный экземпляр класса. Точно как я ожидаю, что указатели будут работать.

Одна работа вокруг, что я пытался это использовать тетсру

void Points::getCoordinates (float *retx, float *rety, float *retz) 
{ 
memcpy(retx, x, sizeof(float)*numPoints); 
memcpy(rety, y, sizeof(float)*numPoints); 
memcpy(retz, z, sizeof(float)*numPoints); 
} 

Это работает так, как я хочу его тоже. Недостатком этого подхода является то, что мне нужно выделить правильный объем памяти в вызывающей функции, и я использую больше памяти, чем мне кажется.

+0

вам необходимо передать входные аргументы либо в виде двойных указателей, либо в качестве ссылки на указатель (т. Е. Вам нужно передать их по ссылке). – 101010

+0

Класс 'Points', который хранит несколько массивов для значений x, y и z, звучит как [плохая идея] (http://msmvps.com/blogs/jon_skeet/archive/2014/06/03/anti-pattern-parallel -collections.aspx). Почему бы не использовать 'std :: vector ' вместо этого, где 'Point' имеет одно значение x, y и z? – fredoverflow

+0

@FredOverflow Я на самом деле не писал класс «Points». Казалось, это самый простой пример. :) – Cecilia

ответ

2

Однако, значения меняются, как только я оставляю эту функцию. Ограничен ли объем указателя?

Это как должно быть.

Рассмотрим этот код:

int f(int a) { a = 20; } 

int x = 10; 
f(x); 

При вызове f, место создается в стеке, где значение в x (10) размещен. Затем это место называется a, и функция выполняется для a, имеющего значение x. Если вы изменяете (как в приведенном выше коде), x не изменяется, потому что x - это другая переменная.

Теперь рассмотрим, что происходит, когда a является указателем:

int g(int *a) { a = nullptr; } 
int *b = new int{10}; 
g(b); 

Здесь значение создается на стеке для a, является адресом памяти. Если вы измените сам адрес (если вы делаете a = nullptr;), как и в случае выше, b не влияет, только a (т. Е. Изменение только действует в теле функции).

Вы можете изменить значение на адресу, указанному. Хотя b и a - разные переменные, при вызове g(b) обе переменные (хотя и разные) будут содержать один и тот же адрес памяти. Таким образом, если вы измените значение по адресу, указанному a, вы фактически вернете значение для кода клиента (вы его не возвращаете, но клиентский код будет иметь к нему доступ, путем де-ссылки b) ,

Код:

int g(int *a) { *a = 20; } // assign value to memory pointed to, by a 
int *b = new int{10}; 
g(b); // a and b will point to the same value 
assert(*b == 20); // evaluates to true 

Это стиль C использования выходного параметра. В C выходной параметр является параметром, содержащим адрес, так что изменение значения по этому адресу позволяет клиенту-коду получить доступ к этому адресу (как в моем примере выше).

В вашей конкретной ситуации, ваш код может быть написан как это (но, вероятно, не следует - смотрите ниже):

void Points::getCoordinates (float **retx, float **rety, float **retz) 
{ 
    *retx=x; 
    *rety=y; 
    *retz=z; 
} 

Каждый параметр имеет дополнительный уровень косвенности (то есть, если вы хотите, чтобы ваш выход параметр для размещения адреса для float, вы должны принять в качестве аргумента указатель на этот адрес - т.е. двойной указатель или указатель на указатель на float).

Почему вы не должны:

C++ введены ссылки, которые позволяют адрес будет рассматриваться как сам объект. Так, C++, код может выглядеть следующим образом:

void Points::getCoordinates (float *&retx, float *&rety, float *&retz) 
{ 
    retx=x; 
    rety=y; 
    retz=z; 
} 

еще лучше, в C++ код, вероятно, должен выглядеть следующим образом (не уверен, если это возможно для вашего кода/кода базы):

struct Point3d { float x, y, z; } 
Point3d Points::getCoordinates() const { return Point3d{ x, y, z }; } 
2

Вам нужно использовать ссылки указателя.

void Points:: getCoordinates (float *&retx, float *&rety, float *&retz) 
+0

Единственное, что я может добавить, что в зависимости от потребностей OP может быть лучше передать 'const float * &', чтобы предотвратить непреднамеренное изменение внутреннего состояния объекта 'Points', используя параметры out (очевидно, если это поведение * желательно * , оставьте 'const' out). – dlf

Смежные вопросы