2010-09-21 13 views
5

Vengo de un C# de fondo a C++. Digamos que tengo un método que crea un objeto en un método en la pila, luego lo paso a otro método de clases que lo agrega a un vector memeber.Creando un objeto en la pila pasando luego por referencia a otro método en C++

void DoStuff() 
{ 
    SimpleObj so = SimpleObj("Data", 4); 
    memobj.Add(so); 
} 

//In memobj 
void Add(SimpleObj& so) 
{ 
    memVec.push_back(so); //boost::ptr_vector object 
} 

Aquí están mis preguntas:

  1. Una vez que los métodos extremos DoStuff será el así salir de su ámbito y hacer estallar de la pila?
  2. memVec tiene un puntero a eso, pero se saltó lo que sucede aquí?
  3. ¿Cuál es la forma correcta de pasar objetos de pila a métodos que los almacenarán como punteros?

Me doy cuenta de que estos son probablemente obvios para un programador de C++ con cierta experiencia.

Marcos

+5

solo como una nota al margen, "SimpleObj so = SimpleObj (" Datos ", 4);" no es muy C++ ish, use "SimpleObj así que (" Datos ", 4);" – Milan

+0

Gracias, todavía tengo algo de resaca C#. – Mark

+0

@ milan1612: desafortunadamente, sigue siendo la notación más portátil. un montón de compiladores de C++ falla en este último, [como en el caso] (http://www.parashift.com/c++faq-lite/ctors.html#faq-10.21). – Dummy00001

Respuesta

8
  1. Sí.
  2. El puntero permanece "vivo", pero apunta a un objeto que ya no existe. Esto significa que la primera vez que intenta desreferenciar dicho puntero obtendrá un comportamiento indefinido (es probable que su programa se bloquee, o, lo que es peor, continuará ejecutándose con resultados "extraños").
  3. Simplemente no hagas eso si deseas conservarlos después de que se devuelva la función. Es por eso que se usan la asignación de montón y los contenedores que almacenan copias de los objetos.

La forma más sencilla de lograr lo que usted está tratando de hacer sería para almacenar una copia de los objetos en un contenedor STL normal (por ejemplo, std::vector). Si dichos objetos son pesados ​​y costosos de copiar, es posible que desee asignarlos en el almacenamiento dinámico en un contenedor de punteros inteligentes adecuados, p. boost::shared_ptr (vea el ejemplo en @Space_C0wb0y's answer).

Otra posibilidad es utilizar el boost::ptr_vector en asociación con boost::ptr_vector_owner; esta última clase se ocupa de "ser dueño" de los objetos almacenados en el ptr_vector asociado, y eliminar todos los punteros cuando se sale del alcance. Para obtener más información sobre ptr_vector y ptr_vector_owner, es posible que desee echar un vistazo a this article.

+0

Gracias, quiero evitar copiar objetos, pero también evitar tener que administrar la memoria en el montón, así que creo que veré las dos últimas opciones. – Mark

+0

Como vienes de C#, te recomiendo que también eches un vistazo a los otros indicadores inteligentes proporcionados por boost y aprendas dónde se usa mejor cada uno de ellos. La administración de memoria en C++ no es tan fácil como en C#, pero al usar los punteros inteligentes correctos se vuelve casi automática. Por cierto, si mi respuesta u otra resolvió su problema, puede considerar marcarlo como "aceptado". –

+0

Sí, estoy haciendo uso de Boost lib, y el puntero inteligente, simplemente no los entiendo completamente, pero me han iluminado, gracias. – Mark

3

Para lograr su objetivo, se debe utilizar un shared_ptr:

void DoStuff() 
{ 
    boost::shared_ptr<SimpleObj> so(new SimpleObj("Data", 4)); 
    memobj.Add(so); 
} 

//In memobj 
void Add(boost::shared_ptr<SimpleObj> so) 
{ 
    memVec.push_back(so); // std::vector<boost::shared_ptr<SimpleObj> > memVec; 
} 
+0

Ok, eso tiene sentido, por lo que el recuento de referencias será uno en el ptr una vez agregado al vector, luego no se destruirá. – Mark

+0

@Mark: Sí. Y cuando el contenedor se destruye, el recuento de referencias para cada 'shared_ptr' en el contenedor se reducirá en uno (y los objetos se eliminarán en el acto si llega a 0). –

+0

Recomendaría 'unique_ptr' en lugar de' shared_ptr' si está usando C++ 0x - hay menos sobrecarga. En lugar de hacer copias que aumentan el recuento de referencias, simplemente se mueve. – AshleysBrain

0

Para almacenar un puntero a un objeto, debe crearse con nuevo. De lo contrario, desaparecerá cuando salga del alcance.
La solución más fácil a su problema como se presenta aquí sería utilizar std :: vector en lugar de impulso :: ptr_vector, porque de esta manera el push_back sería copiar el objeto en el vector

1

Si el modo de objeto se se desprendió la pila una vez que su función abandona el alcance. Debe crear un objeto Heap usando new y agregar un puntero a ese en su vector.

Como se ha dicho antes, el puntero en su vector apuntará a algo indefinido una vez que su primera función se sale del ámbito

1

Ese código no compilará porque dentro de la función Add usted está tratando de tratar de empujar una objeto entero en un vector que espera un puntero a un objeto.

Si, en cambio, tomas la dirección de ese objeto y la insertas en el vector, entonces sería peligroso ya que el objeto original pronto saldría de la pila y el puntero que almacenaras señalaría a la memoria no inicializada.

Si estaba utilizando un vector normal en lugar de un vector de puntero, la llamada push_back estaría copiando el objeto completo en lugar del puntero y, por lo tanto, sería seguro. Sin embargo, esto no es necesariamente eficiente, y el enfoque de "copiar todo" probablemente no sea intuitivo para alguien de los mundos C#, Java o Python.

Cuestiones relacionadas