Saltar a contenido

Última actualización:2026-10-12

Punteros inteligentes como parámetros de función en C++

C++那些事: cuándo usar punteros inteligentes como parámetros de función

Normalmente vemos que algunas funciones usan punteros inteligentes como parámetros y otras usan punteros crudos; ¿cuál usar? ¿Cuál es el principio a seguir?

Esta sección usa los tips de abseil para explicarlo.

¿Qué problema tiene el siguiente código?

bool CanYouPetTheDog(const std::shared_ptr<Dog>& dog,
                     absl::Duration min_delay) {
  return dog->GetLastPetTime() + min_delay < absl::Now();
}

La función CanYouPetTheDog no afecta a la propiedad (ownership) de su parámetro dog, pero su firma exige almacenarlo en un std::shared_ptr. Esto crea una dependencia innecesaria de un modelo de propiedad concreto, aunque la función en sí no la necesite. Esa dependencia impide que quienes llaman usen otros modelos, como std::unique_ptr o construir el objeto en la pila.

Usar puntero/referencia

Cuando la propiedad no se ve afectada, use referencia o puntero. Al usar referencia/puntero eliminamos la dependencia de un modelo de propiedad concreto y permitimos que nuestra función trabaje con cualquier objeto de tipo Dog.

bool CanYouPetTheDog(const Dog& dog, absl::Duration min_delay) {
  return dog.GetLastPetTime() + min_delay < absl::Now();
}

Con la definición anterior, la función puede llamarse independientemente del modelo de propiedad de quien llama:

Dog stack_dog;
if (CanYouPetTheDog(stack_dog, delay)) { ... }

auto heap_dog = std::make_unique<Dog>();
if (CanYouPetTheDog(*heap_dog, delay)) { ... }

CustomPetPtr<Dog> custom_dog = CreateDog();
if (CanYouPetTheDog(*custom_dog, delay)) { ... }

Si la función modifica el valor pasado, pase una referencia mutable o un puntero crudo, con el mismo uso que el anterior.

Usar punteros inteligentes

Use punteros inteligentes cuando la función modifica la propiedad; el siguiente código ofrece varias sobrecargas para distintos parámetros de puntero inteligente. La primera sobrecarga asume la toma de posesión del objeto pasado; la segunda añade una referencia compartida al objeto pasado. Ambas operaciones dependen de cómo la parte llamante gestione la propiedad del Dog. No se puede tomar posesión de un Dog ubicado en la pila, porque su propiedad no puede retirarse de la pila.

class Human {
 public:
  ...
  void Adopt(std::unique_ptr<Dog> dog) {
    pets_.push_back(std::move(dog));
  }
  void Adopt(std::shared_ptr<Cat> cat) {
    pets_.push_back(std::move(cat));
  }

 private:
  std::vector<std::shared_ptr<Pet>> pets_;
  ...
};

Resumen

Si la propiedad no se transfiere ni se modifica, conviene evitar los punteros inteligentes como parámetros de función.

https://abseil.io/tips/188

Temas relacionados

Comentarios