Рейтинг:
1
Jochen Arndt
Если вы избегаете использования выделенных переменных-членов, то деструктор членов вызывается автоматически, что снижает вероятность утечки памяти. С вашим obj
пример похоже, что вы можете сделать это, изменив член из *ContainerAttributes
к ContainerAttributes
.
Вы также можете подумать о том, чтобы сделать свой ContainerAttributes
класс std::map
основывающийся:
class NOCTURNALSYNERGY_API ContainerAttributes : public std::map<std::string, int>
{
public:
ContainerAttributes();
~ContainerAttributes();
void SetValue(const std::string& name, int value)
{
auto it = find(name);
if (it != end())
it->second = value;
else
insert(std::pair<std::string, int>(name, value));
}
int GetValue(const std::string& name) const
{
const auto it = find(name);
return it == end() ? -1 : it->second;
}
};
Однако это сделает доступ к карте общедоступным. Обратите также внимание, что я изменил
name
параметры
const
и передается по ссылке, которая здесь является подходящей передачей.
Согласно
FString | Unreal Engine[
^] документация, которую он предоставляет
+=
оператор (который, вероятно, просто звонит
Append()
внутренне). Таким образом, вы можете использовать
+
оператор. Но в вашем случае
DebugHelper::Debug(it->first + ": " + it->second);
операция выполняется на (и как)
std::string
и не включил
FString
.
Когда использовать
auto
а когда нет-это ваше решение и обычно выбирается в зависимости от контекста. Просто поищите в интернете что-нибудь вроде "c++ auto or not". Это, наверное, хорошо читать:
Не используйте <auto>, Если вы не имеете в виду это — Джозеф Мэнсфилд[
^].
[no name]
Очень полезно, спасибо. Всего несколько вещей, что вы подразумеваете под изменением члена с *ContainerAttributes на ContainerAttributes?
Кроме того, является ли идея "const auto it" главным образом для отображения намерения, которое возможно, потому что оно->second не модифицируется, а только читается?
Jochen Arndt
Член вашего класса obj на самом деле является указателем (используя new). Но если у вас есть только один экземпляр (не массив), нет необходимости использовать указатель / выделенный экземпляр. Просто используйте один экземпляр в качестве члена:
class obj
{
// ...
// was:
//ContainerAttributes* containerAttributes;
// better:
ContainerAttributes containerAttributes;
};
Затем конструктор вызывается из конструктора obj. И то же самое для деструктора. Нет необходимости звонить
delete containerAttributes;
в деструкторе obj. Деструктор ContainerAttributes вызывается автоматически при удалении std::map.
const
указывает компилятору не разрешать изменять объект. Он должен всегда использоваться, когда объект не изменяется.
[РЕДАКТИРОВАТЬ]
В этом частном случае на самом деле нет необходимости использовать const для итератора, потому что я сделал функцию GetValue() const. В результате компилятор автоматически выберет перегрузку find (), которая возвращает итератор const при использовании auto. Таким образом, это хороший пример преимущества использования auto (в рамках функций-членов const).
[/РЕДАКТИРОВАТЬ]
CPallini
Что о
пустота метода setValue(константные СТД::строка&амп; название, значение типа int)
{
this->operator[](name) = значение;
}
?
Jochen Arndt
Мой виртуальный 5!
[no name]
CPallini, почему вы используете const в этой ситуации для строки std::, но не для int?
CPallini
Const не имеет смысла для параметра, передаваемого по значению.
[no name]
Итак, из вашего примера я понял, что вы передаете строку std::по ссылке, которая не должна быть изменена. Почему бы вам просто не передать его по значению?
CPallini
Потому что это было бы дороже (была бы сделана свежая копия переданной строки).
Jochen Arndt
Если вы передадите его по значению, то копия должна быть создана (что включает вызов конструктора и деструктора, которые вызывают new() и delete с динамическими объектами, такими как std::string), в то время как передача ссылки (или указателя) просто передает адрес (целое число). Таким образом, использование ссылки происходит быстрее, производит меньше кода и потребляет меньше памяти.
Поскольку функция может изменить содержимое переменной, передаваемой по ссылке или указателю, const избегает этого (ошибка компиляции) и сообщает также разработчику, использующему эту функцию, что переданная переменная не изменяется.
Общее правило:
Передавайте фундаментальные типы, такие как bool, char, wchar_t, int, long, float и double по значению, а все остальные-по ссылке (предпочтительно) или указателю. Используйте const со ссылками и указателями, когда значение не изменяется.
[no name]
Ладно, отлично, это имеет смысл, теперь я изменил свой код. Все ли параметры выглядят правильно?
то
char*&
предназначен для обозначения массива символов, передаваемого по ссылке, используемого, например:
DebugHelper::Debug("this is a char*");
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ConversionHelper.h"
#include <string>
#include "Runtime/Engine/Classes/Engine/Engine.h"
#include "CoreMinimal.h"
class NOCTURNALSYNERGY_API DebugHelper
{
public:
static void Debug(const FString& f);
static void Debug(const std::string& s);
static void Debug(const char*& c);
static void Debug(int i);
private:
static void _Debug(const FString& f);
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "DebugHelper.h"
void DebugHelper::Debug(const FString& f)
{
DebugHelper::_Debug(f);
}
void DebugHelper::Debug(const std::string& s)
{
DebugHelper::_Debug(ConversionHelper::StringToFString(s));
}
void DebugHelper::Debug(const char*& c)
{
DebugHelper::_Debug(ConversionHelper::StringToFString(c));
}
void DebugHelper::Debug(int i)
{
DebugHelper::_Debug(ConversionHelper::IntToFString(i));
}
void DebugHelper::_Debug(const FString& f)
{
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, f);
UE_LOG(LogTemp, Log, TEXT("DEBUG | %s"), *f);
}
Jochen Arndt
Это, как правило, не имеет никакого смысла, чтобы передать ссылку на указатель. В таких случаях просто передайте указатель (потому что это адрес, который является целым числом, которое является фундаментальным типом):
void DebugHelper::Debug(const char* s)
[no name]
Я пытался объявить массив char, как
const char[] s
причиной синтаксической ошибки
Jochen Arndt
Это недопустимая строка кода C/C++.
Для параметров функции можно использовать char* и char [] (они эквивалентны).
[no name]
Хорошо, это одна из тех странных вещей, которая является кривой обучения c++. Поправьте меня, если я ошибаюсь, но я понимаю, что char* - это одновременно указатель на символ и символ "массив", но на самом деле он указывает на первый символ в массиве.
В противном случае я не вижу разницы между указателем char и массивом char
Edit: я только что видел пример, и [] стоит после имени параметра, а не типа, так что
const type[] whatever
это неправильно, тогда как
const type whatever[]
правильно.
Кроме того, я бы не проходил здесь по ссылке, потому что char-это родной тип, не так ли?
Jochen Arndt
Тип не имеет значения. Массив всегда передается как указатель за кулисами.
Видишь ли https://www.cs.bu.edu/teaching/cpp/string/array-vs-ptr/
[no name]
Итак, возвращаясь к тому, что было сказано о передаче по ссылке, чтобы избежать копирования, если тип не является родным типом, должен ли я использовать
const char* charArray;
или
const char*& charArray
?
Я предполагаю, что последнее точно такое же, как
const char& charArray[]
Jochen Arndt
Просто используйте char* (или массив, но это не так часто встречается).
Как уже было сказано:
Указатели являются фундаментальными типами и не нуждаются в передаче в качестве ссылки, а массивы, передаваемые в качестве параметров функций, передаются в качестве указателей.
Хотя передача ссылки на указатель возможна, это затрудняет чтение кода и не является обычным явлением.
Также довольно редко используется синтаксис массива для параметров функций (больше в C++, чем в C), но иногда он используется при наличии многомерного массива.
[no name]
User-2223753, конечно, если конструктор для containerAttributes вызывается автоматически, то нет никакого контроля над тем, когда он создается. В других языках ContainerAttributes containerAttributes просто создает объект, но не создает его; как вы можете управлять этим здесь?
Jochen Arndt
Существует контроль: он создается, когда создается экземпляр объекта, имеющего его в качестве члена.
В вашем случае:
При создании экземпляра obj конструктор этого класса вызывает конструкторы всех членов (если таковые имеются). Это требует, чтобы у этих членов не было конструктора или хотя бы одного без параметров.
[no name]
Я имею в виду, что если вы создадите obj, то у вас не будет никакого контроля над отдельным построением элемента в любое время, так как это произойдет немедленно. Вот почему я предпочел бы использовать
ContainerAttributes* containerAttributes;
и когда захочу,
containerAttributes = new ContainerAttributes();
Jochen Arndt
Обычно нет необходимости создавать его позже, когда имеется один экземпляр (а не массив переменного размера) или объект довольно велик.
Одной из ваших целей было избежать утечки памяти. И не используя new() здесь это делается. С указателями вы также должны убедиться, что они инициализированы с помощью nullptr (в конструкторе), и все обращения должны быть защищены проверкой допустимого указателя (по крайней мере, для отладочных сборок). Отказ от использования new() приводит также к меньшему количеству кода (как в исходном, так и в сгенерированном) и более быстрому выполнению.
Поэтому не используйте new (), когда в этом нет необходимости.