3

我目前正在使用 Visual Studio 2017 学习 C++,来自 C#/Java 背景。

我有一个关于在堆上创建对象并在以后正确引用它们的问题。到目前为止,我遇到了多个教程和做事方式。有些人建议尽可能多地使用智能指针,有些人则发誓它是魔鬼工具

我目前的主要看起来像这样:

//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>(比共享更好)替换原始指针。

这是 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 如果您查看更新后的 main,您的意思是我应该返回 astd::unique_ptr而不是 a* (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++ 吗?

4

1 回答 1

5

我认为了解 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和几乎已被弃用。只有图书馆作者才应该在非常极端的情况下使用它们。应该是在堆上分配对象的唯一方法,与 STL 容器(如和.new[]deletedelete[]std::make_std::vectorstd::list

于 2017-07-19T16:59:46.870 回答