Короткий ответ
Нет смысла передавать по ссылке на указатель, а не по указателю, если вы не хотите изменять сам исходный указатель.
Длинный ответ
Основываясь на том, как сформулирован ваш вопрос, а также на вашем первом ответе на комментарии к вашему вопросу, у вас есть фундаментальное неправильное понимание того, что на самом деле являются указателями и ссылками. Вопреки тому, во что вы, кажется, верите, они сами не являются объектами.
Давайте немного вернемся к основам.
Существует две основные области памяти, к которым у нас есть доступ для хранения данных: память стека и память кучи.
Когда мы объявляем переменные, мы получаем выделенное пространство в памяти стека и можем получить доступ к данным, используя имена переменных. Память автоматически освобождается, когда переменная выходит из области видимости.
void myFunction() {
MyClass instance; // allocated in Stack memory
instance.doSomething();
} // instance gets automatically de-allocated here
Самая большая проблема в том, что объем стековой памяти чрезвычайно ограничен по сравнению с обычными программными потребностями, и тот факт, что вам часто нужны данные для сохранения вне определенных областей, что создание экземпляров больших классов в стековой памяти, как правило, плохая идея. Вот где Куча становится полезной.
К сожалению, с памятью кучи, вам нужно занять время жизни выделения памяти. У вас также нет прямого доступа к данным в памяти кучи, вам нужен своего рода ступенька, чтобы добраться туда. Вы должны явно запросить у ОС выделение памяти, а затем явно сказать ей, чтобы она была выделена, когда вы закончите. C ++ дает нам два оператора для этого: new
и delete
.
void myFunction(){
MyClass *instance = new MyClass(); // ask OS for memory from heap
instance->doSomething();
delete instance; // tell OS that you don't need the heap memory anymore
}
Как вы, очевидно, понимаете, в данном случае экземпляр называется pointer
. Кажется, вы не понимаете, что указатель - это не экземпляр самого объекта, это «ступенька» к объекту. Назначение указателя состоит в том, чтобы удерживать этот адрес памяти, чтобы мы не теряли его и позволяли нам добраться до этой памяти путем разыменования места в памяти.
В C ++ есть два способа сделать это: вы либо отмените ссылку на весь указатель, а затем получите доступ к членам объекта так же, как к объекту в стеке; или вы могли бы использовать оператор разыменования члена и получить доступ к члену с помощью этого.
void myFunction(){
MyClass *instance = new MyClass();
(*instance).doSomething(); // older-style dereference-then-member-access
instance->doSomethingElse(); // newer-style using member dereference operator
}
Сами указатели являются просто особыми экземплярами целых чисел. Значения, которые они содержат, являются адресами памяти в памяти кучи, где вы размещаете свои объекты. Их размер зависит от платформы, для которой вы скомпилировали (обычно 32-битную или 64-битную), поэтому их передача не более дорогая, чем передача целого числа.
Я не могу не подчеркнуть, что переменные-указатели не являются самими объектами, они расположены в памяти стека и ведут себя точно так же, как и любая другая переменная стека, когда они выходят из области видимости.
void myFunction() {
MyClass *instance = new MyClass(); // pointer-sized integer of type 'pointer-to-MyClass' created in Stack memory
instance->doSomething();
} // instance is automatically de-allocated when going out of scope.
// Oops! We didn't explicitly de-allocate the object that 'instance' was pointing to
// so we've lost knowledge of it's memory location. It is still allocated in Heap
// memory but we have no idea where anymore so that memory is now 'leaked'
Теперь, поскольку скрытые указатели являются не более чем целыми числами специального назначения, передавая их как не дороже, чем передавая любые другие типы целых чисел.
void myFunction(){
MyClass *instance = new MyClass(); // 'instance' is allocated on the Stack, and assigned memory location of new Heap allocation
instance->doSomething();
AnotherFunction(instance);
delete instance; // Heap memory pointed to is explicitly de-allocated
} // 'instance' is automatically de-allocated on Stack
void anotherFunction(MyClass *inst){ // 'inst' is a new pointer-to-MyClass on the Stack with a copy of the memory location passed in
inst->doSomethingElse();
} // 'inst' is automatically de-allocted
Пока что я не упомянул ссылки, потому что они в общем и целом совпадают с указателями. Они также являются целыми числами, но упрощают использование, делая синтаксис доступа к элементам таким же, как и у переменных стека. В отличие от обычных указателей, ссылки должны быть инициализированы с правильным местоположением памяти, и это местоположение не может быть изменено.
Функционально эквивалентны следующие:
MyClass &instance
MyClass * const instance
Ссылки на указатели являются двойными косвенными указателями, они по сути являются указателями на указатели и полезны, если вы хотите иметь возможность манипулировать не только объектом Heap, но также указателем, содержащим область памяти для этого объекта heap.
void myFunction(){
QString *str = new QString("First string"); // str is allocated in Stack memory and assigned the memory location to a new QString object allocated in Heap memory
substituteString(str);
delete str; // de-allocate the memory of QString("Second String"). 'str' now points to an invalid memory location
} // str is de-allocated automatically from Stack memory
void substituteString(QString *&externalString){ // 'externalString' is allocated in Stack memory and contains memory location of 'str' from MyFunction()
delete externalString; // de-allocate the Heap memory of QString("First string"). 'str' now points to an invalid location
externalString = new QString("Second string"); // Allocate new Heap memory for a new QString object. 'str' in MyFunction() now points to this new location
} // externalString is de-allocated automatically from Stack memory
Если я четко объяснил себе, и вы до сих пор следовали за мной, вы должны понимать, что в вашем случае, когда вы передаете указатель на QAction
функции, вы не копируете QAction
объект, вы только копируете указатель на эту область памяти.Поскольку указатели - это просто целые числа внутри, вы копируете только то, что имеет размер 32 или 64 бита (в зависимости от настроек вашего проекта), и изменение его на ссылку на указатель не будет иметь абсолютно никакого значения.