Ответ 1
В этом конкретном случае вы можете воспользоваться тем фактом, что компиляторы в настоящее время достаточно умны, чтобы оптимизировать для него. Оптимизация называется с именем оптимизацией возвращаемого значения (NRVO), поэтому вполне нормально возвращать такие "большие" объекты. Компилятор может видеть такие возможности (особенно в том, что так просто, как ваш фрагмент кода), и генерировать двоичный файл, чтобы никакие копии не выполнялись.
Вы также можете вернуть неназванные временные файлы:
Object f()
{
return Object();
}
Это вызывает (без названия) оптимизацию возвращаемого значения (RVO) практически для всех современных компиляторов С++. На самом деле Visual С++ реализует эту конкретную оптимизацию, даже если все оптимизации отключены.
Эти виды оптимизации специально разрешены стандартом С++:
ISO 14882: 2003 Стандарт С++, §12.8 п. 15: Копирование объектов класса
Когда выполняются определенные критерии, реализации разрешено опустить скопировать построение объекта класса, даже если конструктор копирования и/или деструктор для объекта имеет сторону последствия. В таких случаях реализация рассматривает источник и цель операции с опущенной копией как просто два разных способа ссылаясь на тот же объект, и разрушение этого объекта происходит позже того времени, когда объекты были бы уничтожены без оптимизации. Элисон операций копирования в следующие обстоятельства (которые могут быть в сочетании с копии):
- в выражении
return
в функции типа terturn класса, когда выражение является именем энергонезависимый автоматический объект с такой же cv-неквалифицированный тип, как возвращаемый тип функции, копия операция может быть опущена построение автоматического объекта непосредственно в функцию return Значение- когда объект временного класса, который не привязан к ссылке будет скопирован в объект класса с тот же самый cv-unqualitied тип, копия операция может быть опущена построение временного объекта прямо в цель пропущенная копия.
Как правило, компилятор всегда будет пытаться реализовать NRVO и/или RVO, хотя в определенных случаях это может не произойти, как несколько путей возврата. Тем не менее, это очень полезная оптимизация, и вы не должны бояться ее использовать.
Если вы сомневаетесь, вы всегда можете проверить свой компилятор, вставив "отладочные заявления" и убедитесь сами:
class Foo
{
public:
Foo() { ::printf("default constructor\n"); }
// "Rule of 3" for copyable objects
~Foo() { ::printf("destructor\n"); }
Foo(const Foo&) { ::printf("copy constructor\n"); }
Foo& operator=(const Foo&) { ::printf("copy assignment\n"); }
};
Foo getFoo()
{
return Foo();
}
int main()
{
Foo f = getFoo();
}
Если возвращаемый объект не предназначен для копирования, или (N) RVO не работает (что, вероятно, вряд ли произойдет), вы можете попробовать вернуть прокси-объект:
struct ObjectProxy
{
private:
ObjectProxy() {}
friend class Object; // Allow Object class to grab the resource.
friend ObjectProxy f(); // Only f() can create instances of this class.
};
class Object
{
public:
Object() { ::printf("default constructor\n"); }
~Object() { ::printf("destructor\n"); }
// copy functions undefined to prevent copies
Object(const Object&);
Object& operator=(const Object&);
// but we can accept a proxy
Object(const ObjectProxy&)
{
::printf("proxy constructor\n");
// Grab resource from the ObjectProxy.
}
};
ObjectProxy f()
{
// Acquire large/complex resource like files
// and store a reference to it in ObjectProxy.
return ObjectProxy();
}
int main()
{
Object o = f();
}
Конечно, это не совсем очевидно, поэтому потребуется соответствующая документация (по крайней мере, комментарий об этом).
Вы также можете вернуть какой-либо умный указатель (например, std::auto_ptr
или boost::shared_ptr
или что-то подобное) объекту, выделенному в свободном хранилище. Это необходимо, если вам нужно вернуть экземпляры производных типов:
class Base {};
class Derived : public Base {};
// or boost::shared_ptr or any other smart pointer
std::auto_ptr<Base> f()
{
return std::auto_ptr<Base>(new Derived);
}