2011-04-24 18 views
13

Soy bastante noobish cuando se trata de C++, ¿cuál es la mejor forma de devolver un objeto? Vengo del mundo de los guiones donde los Objetos siempre son referencias, y trato de lograr la misma noción ... Estoy basando esto en When to pass by reference and when to pass by pointer in C++?, donde un usuario dijo: "Una buena regla general:" Usar referencias cuando puedes y punteros cuando tienes que "."¿La mejor manera de devolver un objeto en C++?

// basic layer class 
class Layer { private: Channel channel; // NEVER NULL }; 

// return object by pointer 
Channel *Layer::getChannel() { 
    return &channel; 
}; 

// return by reference 
Channel& Layer::getChannel() { 
    return channel; 
}; 

El problema con la segunda versión es que el compilador aceptará esta línea:

Channel channel = layer.getChannel(); // creates a copy BAD 

cuando debería ser:

Channel &channel = layer.getChannel(); // reference good 

¿Hay alguna manera de hacer cumplir la persona que llama de la segunda opción para forzarlo a no crear un nuevo canal, o es la primera opción mejor de todos modos, incluso si nunca será NULL?

+9

"Estoy que viene del mundo de secuencias de comandos donde los objetos son siempre referencias, y estoy tratando de lograr la misma noción de" ¿Por qué estás tratando de pasar todo a su alrededor como referencias en C++? No es natural hacer eso en C++. La "regla de oro" que cite probablemente se establezca mejor como "Usar valores (y copias) siempre que sea posible, de lo contrario use referencias, y solo como último recurso use punteros". –

+0

Teniendo en cuenta su experiencia, le sugiero encarecidamente que aprenda acerca de ** punteros inteligentes ** para que obtenga administración de memoria automática para los objetos asignados en el montón. –

Respuesta

11

es necesario ajustar la clase Channel sí de modo que no es copiable. Si se puede copiar, el usuario puede copiarlo, y nada de lo que haga puede evitarlo.

Si el copiado no es una operación significativa, entonces puede "desactivarlo". Simplemente defina el constructor de copia (Channel(const Channel&)) y el operador de asignación (Channel& operator=(const Channel&)) para que sea privado. Entonces, cualquier intento de copiar la clase dará como resultado un error de compilación.

En una nota lateral, como han mencionado otros, C++ no es los lenguajes de script que está familiarizado. Todo no es una referencia, y solo te estás preparando para un mundo de dolor pretendiendo lo contrario. En C++, es común asignar objetos en la pila y pasar objetos por valor, en lugar de pasar referencias y punteros.

4

Devolver una referencia (o referencia constante) es la forma normal de que un método getter otorgue a la persona que llama el acceso directo a una variable miembro, por lo que recomendaría la segunda versión de getChannel().

Si desea evitar que las personas que llaman realicen copias inapropiadas de Channel, puede lograrlo haciendo que su copia sea privada. (Si desea evitar todo de hacer copias, incluso Channel sí mismo, puede declarar el constructor privado y luego no implementarlo.) Pero solo debe hacer esto si hacer una copia sería realmente absurdo, p. si la clase representa algún tipo de recurso subyacente que no se puede copiar. No prohíba copiar solo porque cree que la persona que llama no debe necesitar para; esa es la decisión de la persona que llama de hacer.

2

Devuelva una copia del objeto cuando la copia no sea costosa para sus propósitos, y cuando no necesite cambiar el original. Esto debería ser el predeterminado.

Channel Layer::getChannel() {  return channel; }; 

Devuelva por referencia o puntero cuando el copiado sea costoso o cuando desee cambiar el valor. La devolución por referencia le permite hacer cosas como esta:

layer.getChannel().clear(); 

Y actúe en el canal que está en esa capa.

Devolver un puntero es similar a devolver una referencia, excepto que le da un poco más de flexibilidad, ya que el puntero no puede señalar a ningún objeto. A menudo, un puntero cuando quiero poder usar la tienda del "canal" en otra clase. Entonces haría

class MyClass 
{ 
    // ... 
    void setChannel(Channel *pC) { m_pChannel = pC; } 
private: 
    Channel * m_pChannel; // pointer to a channel that came from layer 
} 
1

No se puede detener una persona que llama para crear una nueva instancia, incluso cuando se utiliza el puntero de retorno versión.

Channel* channel = new Channel(*layer.getChannel()); 

Sé que hay una manera de lograr este objetivo. (Por ejemplo, hacer que el ctor de Channle sea privado, de modo que solo su función de miembro estática o sus funciones de amigo pueden crearlo). Sin embargo, no creo que este sea el objetivo de su pregunta.

El punto es que cuando está haciendo que la función miembro devuelva referencia o puntero, le da a las opciones de un llamante que puede elegir si quiere copiarlo o consultarlo. Además, puede hacer que su intención sea más clara al agregar const para que sea de solo lectura.

Para su caso, me gustaría ir para referencia de retorno versión que el canal no puede ser nulo. Si no desea que cambien la variable miembro, devuelva la referencia constante. Recuerde que no existe una única forma mejor de decidir el tipo de valor devuelto, ya que depende de lo que quiera decir. ¡Espero eso ayude! :)

1

Puesto que usted está devolviendo una referencia al objeto, le está dando a los usuarios de la clase de acceso directo al objeto, y si vas a hacer eso, ¿por qué estás haciendo el objeto privado? Solo hazlo público.

+0

Estoy tratando de evitar que cualquier otro objeto establezca Layer.channel. Al devolver una referencia, ¿supongo que todavía se puede establecer la referencia a un nuevo canal correcto? Me duele la cabeza. – ansiart

-1

Lo más importante es mantener la legibilidad con el código que está a su alrededor. "Cuando fueres haz lo que vieres." es importante. Usted lo escribe una vez, pero todos los que tienen que mantener su código tienen que leerlo. Si, de repente, su código sigue pautas diferentes a las que le rodean, eso significa que primero necesita descubrir su estilo, luego descubra lo que está haciendo ...

Un enfoque que he visto funciona muy bien es tener punteros para las cosas que cambiar y referencias const para las cosas que no lo hace:

class Passenger { 
    ... 
}; 

class Car { 
public: 
    int speed() const { return speed_; } 
    void set_speed(int speed) { speed_ = speed; } 
    const Passenger& passenger() const { return pass_;} 
    Passenger* mutable_passenger() { return &pass_; } 

private: 
    int speed_; 
    Passenger pass_; 
}; 

los clientes de esta clase pueden hacer:

const Passenger& pass = car.passenger(); // no copy, but don't need to deal with NULL ptrs. 

Otras respuestas que sugiere hacer la copia de un error de compilación son buenas.

Cuestiones relacionadas