указатели — C ++ работает с объектами в куче

В настоящее время я изучаю C ++ из C # / Java, используя Visual Studio 2017.

У меня есть вопрос относительно создания объектов в куче и ссылки на них должным образом в будущем. До сих пор я сталкивался с несколькими уроками и способами ведения дел. Некоторые рекомендуют использовать умные указатели как можно больше, другие клянутся инструмент дьяволов.

Моя текущая главная выглядит так:

//Main
Person *makePerson()
{
string name;
int age;
cout << "Input name: ";
cin >> name;
cout << "Input age: ";
cin >> age;
return new Person(name, age);
}

Child *makeChild(Person &parent)
{
return new Child(*makePerson(), &parent);;
}

int main()
{
cout << "---Input parent data---" << endl;
Person *person = makePerson();
cout << "printing: " << *person << endl;
cout << "---Input child data---" << endl;
Child *child = makeChild(*person);
cout << "printing: " << *child << endl;
cout << "---end of main---" << endl;
delete person;
delete child;
return 0;
}

Функция обрабатывает ввод персональных данных и возвращает указатель на новый объект Person. Затем у меня есть функция, которая обрабатывает создание дочернего объекта, беря родительскую ссылку и запрашивая у makePerson оставшиеся данные.

Можно ли считать это хорошим C ++? Как я могу сделать это лучше? Я был бы очень признателен за примеры кода.

Как некоторые уже предложили, я мог бы заменить необработанные указатели shared_ptr<Person> person (тяжелый) или unique_ptr<Person> (лучше, чем поделился).

Это код для персоны и дочерних классов. Обратите внимание, что у Child есть необработанный указатель типа Person *parent,

//header
class Person
{
protected:
std::string name;
int age;
public:
Person();
Person(const Person& other);
Person(std::string inName, int inAge);
~Person();
virtual void print() const;
std::string getName() const;
int getAge() const;
Person &operator=(const Person &other);
//overload print functionality, act as if it was toString
friend std::ostream &operator<<(std::ostream &out, const Person &p);
};
//cpp
Person::Person() : name(""), age(0) {
std::cout << "Person empty constructor" << std::endl;
}

Person::Person(std::string inName, int inAge) : name(inName), age(inAge) {
std::cout << "Person (" << name << ") default constructor" << std::endl;
}

Person::Person(const Person & other) : name(other.name), age(other.age) {
std::cout << "Person (" << name << ") copy constructor" << std::endl;
}

Person::~Person() {
std::cout << "Person (" << name << ") destructor" << std::endl;
}

void Person::print() const {
std::cout << name << ", " << age << std::endl;
}

std::string Person::getName() const
{
return name;
}

int Person::getAge() const
{
return age;
}

Person & Person::operator=(const Person & other) {
std::cout << "Person (" << other.name << ") assignment constructor" << std::endl;
name = other.name;
age = other.age;
return *this;
}
std::ostream &operator<<(std::ostream &out, const Person &p) {
return out << p.name << ", " << p.age;
}

Ребенок — это человек, и для ребенка имеет смысл знать, кто является родителем ребенка. Однако я не уверен, как справиться с этим «знанием». Вот код, который я использую для дочернего класса:

//Header
class Child : public Person
{
private:
const Person *parent;
public:
Child();
Child(std::string name, int age);
Child(std::string name, int age, const Person *parent);
Child(const Child &child, const Person *parent);
Child(const Person &person);
~Child();
Child &operator=(const Child &other);
void print() const;
friend std::ostream &operator<<(std::ostream &out, const Child &c);
};
//cpp
Child::Child() {
std::cout << "Child empty constructor" << std::endl;
}

Child::Child(std::string name, int age) : Person(name, age), parent(nullptr) {
std::cout << "Orphan (" << name << ") constructor" << std::endl;
}

Child::Child(std::string name, int age, const Person *parent) :
Person(name, age), parent(parent) {
std::cout << "Child (" << name << ") default constructor" << std::endl;
}

Child::Child(const Child &child, const Person *parent) :
Person(child.name, child.age), parent(parent) {
std::cout << "Child (" << child.name << ") copy constructor" << std::endl;
}

Child::Child(const Person &person) : Person(person), parent(nullptr) {
std::cout << "Child from person (" << name << ") constructor" << std::endl;
}

Child::~Child() {
std::cout << "Child (" << name << ") destructor" << std::endl;
}

Child & Child::operator=(const Child & other) {
name = other.name;
age = other.age;
parent = other.parent;
std::cout << "Child (" << name << ") assignment constructor" << std::endl;
return *this;
}

void Child::print() const {
if(parent)
std::cout << *this << " is child of " << *parent << std::endl;
else
std::cout << *this << " is orphan" << std::endl;
}

std::ostream &operator<<(std::ostream &out, const Child &c) {
return out << c.name << ", " << c.age << " is " <<
(c.parent ? ("child of " + c.parent->getName() + ", " + std::to_string(c.parent->getAge())) : "orphan");
}

Это вывод, который я получаю:

введите описание изображения здесь

Я предполагаю, что мой вопрос все еще остается, может ли кто-нибудь дать мне пример того, как это должно выглядеть, чтобы считаться хорошим C ++?

@ user4581301 если вы посмотрите на обновленную главную страницу, значит, я должен вернуть std::unique_ptrвместо * (raw pointer)? В этом случае моя функция будет выглядеть так:

std::unique_ptr<Person> makePerson2()
{
string name;
int age;
cout << "Input name: ";
cin >> name;
cout << "Input age: ";
cin >> age;
return std::unique_ptr<Person>(new Person(name, age));
}

И объявление переменной как:

std::unique_ptr<Person> upParent = makePerson2();
cout << "printing: " << *upParent << endl;

Будет ли это считаться «лучшим» C ++, чем то, что я имею до сих пор?

2

Решение

Я думаю, что одна из самых важных вещей, которые нужно знать о C ++ особенно если вы пришли из Java / C # фона является:

По умолчанию объекты являются типами значений, а не ссылочными типами!

весь ваш код мог бы быть написан просто:

int main()
{
//this work
Person person("John Doe", 22);
//this work
Child child("Johnny Doe", 2, person);
cout << "---end of main---" << endl;
return 0;
}

Видите, как код превратился в ничто? Вам не нужно беспокоиться о распределении, удалении неиспользуемого объекта и т. д., потому что объекты не являются ссылочными типами для начала!

Моя личная иерархия правил:

  1. максимально использовать объекты как типы значений. передать их по ссылке, чтобы избежать копий. убедитесь, что для соответствующих классов реализован правильный конструктор перемещения. объекты как типы значений + ссылки должны быть стандартным способом программирования на C ++.
  2. если вы не можете использовать ссылку, потому что хотите указать отсутствующий объект, используйте C-указатель. в любом случае, строго соблюдайте минимальные значения C-указателей и никогда не позволяйте им управлять чем-либо. C-указатели в основном являются «зрителями» чего-либо, и они могут просматривать «ничего» (или ноль). всегда думайте, можете ли вы заменить C-указатель на C ++. если ты Можно сделай это, делать Это.
  3. использование std::unique_ptr если по какой-то причине вам нужно динамическое распределение памяти, например, динамический полиморфизм. имейте в виду, что C ++ в основном работает с шаблонами как статический полиморфизм, а не с техникой наследования + переопределения в стиле Java.
  4. использование std::shared_ptr редко, и только когда вы уверены, что есть много владельцев, например, объект, на который ссылаются в разных потоках. std::shared_ptr следует использовать в крайних случаях. всегда копируйте общий указатель. никогда не передавайте общий указатель по ссылке.

Тем не мение, new , new[] , delete а также delete[] в значительной степени устарели. только авторы библиотеки должны использовать их в самых крайних случаях. std::make_ должен быть единственным способом размещения объекта в куче, наряду с контейнерами STL, такими как std::vector а также std::list,

5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]