que - punteros en c++ ejercicios resueltos pdf
¿Es incorrecto desreferenciar un puntero para obtener una referencia? (7)
Preferiría usar referencias en todas partes, pero en el momento en que use un contenedor STL debe usar punteros a menos que realmente desee pasar tipos complejos por valor.
Para que quede claro: los contenedores STL se diseñaron para admitir cierta semántica ("semántica de valores"), como "se pueden copiar elementos en el contenedor". Como las referencias no son reenviables, no son compatibles con la semántica de valores (es decir, intente crear un std::vector<int&>
o std::list<double&>
). Tiene razón al decir que no puede poner referencias en contenedores STL.
En general, si utiliza referencias en lugar de objetos simples, o bien está utilizando clases base y desea evitar el corte, o está intentando evitar la copia. Y, sí, esto significa que si desea almacenar los elementos en un contenedor STL, necesitará utilizar punteros para evitar rebanar y / o copiar.
Y, sí, lo siguiente es legítimo (aunque en este caso, no muy útil):
#include <iostream>
#include <vector>
// note signature, inside this function, i is an int&
// normally I would pass a const reference, but you can''t add
// a "const* int" to a "std::vector<int*>"
void add_to_vector(std::vector<int*>& v, int& i)
{
v.push_back(&i);
}
int main()
{
int x = 5;
std::vector<int*> pointers_to_ints;
// x is passed by reference
// NOTE: this line could have simply been "pointers_to_ints.push_back(&x)"
// I simply wanted to demonstrate (in the body of add_to_vector) that
// taking the address of a reference returns the address of the object the
// reference refers to.
add_to_vector(pointers_to_ints, x);
// get the pointer to x out of the container
int* pointer_to_x = pointers_to_ints[0];
// dereference the pointer and initialize a reference with it
int& ref_to_x = *pointer_to_x;
// use the reference to change the original value (in this case, to change x)
ref_to_x = 42;
// show that x changed
std::cout << x << ''/n'';
}
Ah, y no sabes si los objetos fueron creados dinámicamente o no.
Eso no es importante. En la muestra anterior, x
está en la pila y almacenamos un puntero a x
en los pointers_to_vectors
. Claro, pointers_to_vectors
usa una matriz asignada dinámicamente internamente (y delete[]
s esa matriz cuando el vector
sale del alcance), pero esa matriz contiene los apuntadores, no las cosas apuntadas. Cuando pointers_to_ints
queda fuera del alcance, la int*[]
interna int*[]
es delete[]
-ed, pero las int*
s no son delete
d.
Esto, de hecho, dificulta el uso de punteros con contenedores STL, porque los contenedores STL no administrarán la vida útil de los objetos apuntados. Es posible que desee consultar la biblioteca de contenedores de punteros de Boost. De lo contrario, (1) querrá usar contenedores STL de punteros inteligentes (como boost:shared_ptr
que es legal para contenedores STL) o (2) administrar la vida útil de los objetos apuntados de otra manera. Puede que ya estés haciendo (2).
Preferiría usar referencias en todas partes, pero en el momento en que use un contenedor STL debe usar punteros a menos que realmente desee pasar tipos complejos por valor. Y me siento sucio volviendo a ser una referencia, simplemente parece estar mal.
¿Lo es?
Para aclarar...
MyType *pObj = ...
MyType &obj = *pObj;
¿No es esto ''sucio'', ya que puedes (incluso si solo en teoría, ya que lo verificaras primero) desreferenciar un puntero NULL?
EDITAR: Ah, y usted no sabe si los objetos fueron creados dinámicamente o no.
Asegúrese de que el puntero no sea NULL antes de intentar convertir el puntero a una referencia, y que el objeto permanecerá en el alcance siempre y cuando su referencia lo haga (o permanezca asignado, en referencia al montón), y estará bien. y moralmente limpio :)
Inicializar una referencia con un puntero desreferenciado es absolutamente correcto, no hay nada de malo en ello. Si p
es un puntero, y si eliminarlo es válido (por lo que no es nulo, por ejemplo), entonces *p
es el objeto al que apunta. Puede vincular una referencia a ese objeto del mismo modo que vincula una referencia a cualquier objeto. Obviamente, debe asegurarse de que la referencia no sobreviva al objeto (como cualquier referencia).
Entonces, por ejemplo, supongamos que se me pasa un puntero a una matriz de objetos. Podría ser un par de iteradores, un vector de objetos o un map
de objetos, pero usaré una matriz para simplificar. Cada objeto tiene una función, order
, que devuelve un número entero. Debo llamar a la función de la bar
una vez en cada objeto, en orden de aumentar el valor de la order
:
void bar(Foo &f) {
// does something
}
bool by_order(Foo *lhs, Foo *rhs) {
return lhs->order() < rhs->order();
}
void call_bar_in_order(Foo *array, int count) {
std::vector<Foo*> vec(count); // vector of pointers
for (int i = 0; i < count; ++i) vec[i] = &(array[i]);
std::sort(vec.begin(), vec.end(), by_order);
for (int i = 0; i < count; ++i) bar(*vec[i]);
}
La referencia que mi ejemplo ha inicializado es un parámetro de función en lugar de una variable directamente, pero podría haberlo hecho de forma válida:
for (int i = 0; i < count; ++i) {
Foo &f = *vec[i];
bar(f);
}
Obviamente, un vector<Foo>
sería incorrecto, ya que entonces llamaría a la bar
sobre una copia de cada objeto en orden, no sobre cada objeto en orden. bar
toma una referencia no const, por lo tanto, aparte de la actuación o cualquier otra cosa, claramente estaría mal si bar
modifica la entrada.
Un vector de punteros inteligentes o un vector de puntero de refuerzo también sería incorrecto, ya que no soy el propietario de los objetos del conjunto y, desde luego, no debo liberarlos. También es posible que no se permita ordenar la matriz original, o que sea imposible si se trata de un map
lugar de una matriz.
Mi respuesta no aborda directamente su preocupación inicial, pero parece que se encuentra con este problema porque tiene un contenedor STL que almacena tipos de puntero.
Boost proporciona la biblioteca ptr_container para abordar este tipo de situaciones. Por ejemplo, un ptr_vector
almacena internamente punteros a tipos, pero devuelve referencias a través de su interfaz. Tenga en cuenta que esto implica que el contenedor posee el puntero a la instancia y gestionará su eliminación.
Aquí hay un ejemplo rápido para demostrar esta noción.
#include <string>
#include <boost/ptr_container/ptr_vector.hpp>
void foo()
{
boost::ptr_vector<std::string> strings;
strings.push_back(new std::string("hello world!"));
strings.push_back(new std::string());
const std::string& helloWorld(strings[0]);
std::string& empty(strings[1]);
}
No tiene nada de malo, pero tenga en cuenta que en el nivel de código de máquina, una referencia suele ser la misma que un puntero. Por lo tanto, generalmente el puntero no se desreferencia realmente (sin acceso a memoria) cuando se asigna a una referencia. Entonces, en la vida real, la referencia puede ser 0 y el bloqueo ocurre al usar la referencia, lo que puede suceder mucho más tarde que su asignación.
Por supuesto, lo que sucede depende en gran medida de la versión del compilador y de la plataforma de hardware, así como de las opciones del compilador y el uso exacto de la referencia.
Oficialmente, el comportamiento de desreferenciar un 0-Pointer no está definido y, por lo tanto, cualquier cosa puede suceder. Esto incluye que puede bloquearse inmediatamente, pero también que puede colapsar mucho más tarde o nunca.
Por lo tanto, siempre asegúrese de que nunca asigne un 0-Pointer a una referencia: errores como este son muy difíciles de encontrar.
Editar: hizo el cursivo "usualmente" y el párrafo agregado sobre el comportamiento oficial "indefinido".
No. ¿De qué otra forma podría implementar operator=
? Tienes que desreferenciar this
para devolver una referencia a ti mismo.
Sin embargo, tenga en cuenta que todavía almacenaré los elementos en el contenedor STL por valor, a menos que su objeto sea enorme, la sobrecarga de las asignaciones de montón va a significar que está usando más almacenamiento y es menos eficiente de lo que sería si acabo de almacenar el artículo por valor.
Si desea que el contenedor contenga objetos dinámicamente asignados, no debe usar punteros sin formato. Use unique_ptr
o el tipo similar que sea apropiado.