Pregunta

Me gustaría saber una buena sintaxis para C ++ Getters and Setters.

private:
YourClass *pMember;

El setter es fácil, supongo:

void Member(YourClass *value){
  this->pMember = value; // forget about deleting etc
}

¿Y el Getter? ¿Debo usar referencias o punteros const?

ejemplo:

YourClass &Member(){
   return *this->pMember;
}

o

YourClass *Member() const{
  return this->member;
}

¿cual es la diferencia entre ellos?

Gracias,

José

EDITAR:

Lo siento, editaré mi pregunta ... Sé acerca de referencias y punteros, estaba preguntando sobre referencias y consejos const Pierde si voy de una manera u otra ...

Así que supongo que usaré consejos const en lugar de referencias

Los punteros const no se pueden eliminar o establecer, ¿verdad?

¿Fue útil?

Solución

Como ley general:

  • Si NULL es un parámetro o valor de retorno válido, use punteros.
  • Si nulo es NO Un parámetro o valor de retorno válido, use referencias.

Entonces, si el setter posiblemente se llame con NULL, use un puntero como parámetro. De lo contrario, use una referencia.

Si es válido llamar al obtenido de un objeto que contiene un puntero nulo, debe devolver un puntero. Si dicho caso es un invariante ilegal, el valor de retorno debería ser una referencia. El Getter entonces debe lanzar una excepción, si la variable de miembro es nula.

Otros consejos

Lo mejor es proporcionar una interfaz OO real al cliente que oculta los detalles de implementación. Los getters y los setters no son OO.

Su código parece mucho como si esté acostumbrado a un lenguaje diferente, en C ++ usando this->x (para un ejemplo) es relativamente inusual. Cuando el código está bien escrito, también lo está usando un accesor o mutador.

Aunque soy bastante inusual en este aspecto particular, voy a registrar (una vez más) diciendo que obligar al código del cliente a usar un accesor o mutador directamente es una mala idea. Si honestamente tiene una situación en la que tiene sentido que el código del cliente manipule un valor en su objeto, entonces el código del cliente debe usar la asignación normal para leer y/o escribir ese valor.

Cuando/si necesita controlar qué valor se asigna, la sobrecarga del operador le permite tomar ese control sin forzar la sintaxis fea de Get/Set en el código del cliente. Específicamente, lo que desea es una clase proxy (o plantilla de clase). Solo para un ejemplo, una de las situaciones más comunes en las que las personas quieren obtener/establecer funciones es algo así como un número que se supone que está restringido a algún rango particular. los setXXX verifica el nuevo valor para estar en el rango y el getXXX Devuelve el valor.

Si quieres eso, una plantilla (bastante) simple puede hacer el trabajo de manera mucho más limpia:

template <class T, class less=std::less<T> >
class bounded {
    const T lower_, upper_;
    T val_;

    bool check(T const &value) {
        return less()(value, lower_) || less()(upper_, value);
    }

    void assign(T const &value) {
        if (check(value))
            throw std::domain_error("Out of Range");
        val_ = value;
    }

public:
    bounded(T const &lower, T const &upper) 
        : lower_(lower), upper_(upper) {}

    bounded(bounded const &init) 
        : lower_(init.lower), upper_(init.upper)
    { 
        assign(init); 
    }

    bounded &operator=(T const &v) { assign(v);  return *this; }

    operator T() const { return val_; }

    friend std::istream &operator>>(std::istream &is, bounded &b) {
        T temp;
        is >> temp;

        if (b.check(temp))
            is.setstate(std::ios::failbit);
        else
            b.val_ = temp;
        return is;
    }
};

Esto también hace que el código sea mucho más cerca de la documentación de sí mismo, por ejemplo, cuando declara un objeto como: bounded<int>(1, 1024);, es evidente de inmediato que la intención es un entero en el rango de 1 a 1024. La única parte alguien puede que Encontrar a la pregunta es si 1 y/o 1024 se incluyen en el rango. Esto es considerablemente diferente de definir un INT en la clase, y esperar que todos los que miren la clase se den cuenta de que se supone que deben usar el SETXXX para hacer cumplir algunos límites (en ese punto desconocidos) en los valores que pueden ser asignado.

Cuando incorporas uno de estos en una clase, la convierte en una variable pública, y el rango aún se aplica. En el código del cliente, no hay un argumento real sobre la sintaxis (solo está asignando a una variable pública, como lo haría con cualquier otro, con el detalle menor de que intentar asignar un valor que está fuera de rango lanzará una excepción. En teoría, la clase probablemente debería tomar una plantilla de política-parámetro para especificar exactamente lo que hace en ese caso, pero nunca he tenido una razón real para molestarme con eso.

Como otros han dicho, use punteros si NULL es una posibilidad.

En la mayoría de los casos, prefiero usar referencias cuando sea posible. Personalmente, en mi código, me gusta usar la distinción entre punteros y referencias a la propiedad de la señal. Pienso en las llamadas con referencias como "prestar" un objeto a otra función o clase. La clase original que aprobó o devolvió la referencia aún poseerla, y es responsable de su creación, mantenimiento y limpieza. Cuando mi código pasa un puntero que no es de Const, por otro lado, generalmente significa que hay algún tipo de transferencia o intercambio de propiedades, con todas las responsabilidades que conlleva.

(Y sí, generalmente uso punteros inteligentes. Son similares a las referencias en mi mente. Estoy hablando del código de nivel inferior que el aquí).

¿cual es la diferencia entre ellos?

La referencia es un alias de la cosa ( es la cosa*). Un puntero es la dirección de la cosa. Si existe la posibilidad de que lo que señala no esté allí, entonces probablemente no quiera devolver referencias. Las referencias le dicen a la persona que llama "Te voy a dar un alias que existirá cuando te lo devuelva". De hecho, realmente no hay forma de verificar la referencia para ver si lo que está subyacente es válido.

Con el puntero, semánticamente, está implicando que la persona que llama puede verificar si el miembro existe antes de usarlo. Usamente, esto se hace con un cheque nulo.

En última instancia, no hay una respuesta "correcta". Depende del contrato de la clase y si la persona que llama/debería/quiere verificar si "Miembro" todavía está cerca.

La respuesta corta son los punteros para cosas que se pueden señalar en otro lugar y referencias para alias "sin cuenta".

Además de las otras respuestas, si elige referencias para el Getter, no lo escriba como en su ejemplo:

YourClass &Member(){
   return *this->pMember;
}

Tu getter realmente permite configurar, como en instance->Member() = YourClass(); y así evitando a tu setter. Esto podría no estar permitido si su clase no puede copiar, pero aún es otra cosa que tenga en cuenta. Otro inconveniente es que el Getter no es constante.

En su lugar, escriba su Getter así:

const YourClass &Member() const {
   return *this->pMember;
}

+1 al cuestionar el uso de setters y getters. Si debe usarlos y hacer que la posibilidad de nulos considere usar Boost :: Shared_PTR. De esta manera, la propiedad se maneja para usted.

Jonathan, ¿qué compilador estás usando? Hay una gran posibilidad de que shared_ptr Ya viene enviado con él como parte de la implementación TR1 del compilador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top