C ++使用堆上的对象

时间:2017-07-19 16:53:26

标签: c++ pointers heap shared-ptr raw-pointer

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

我有一个关于在堆上创建对象并在路上正确引用它们的问题。到目前为止,我遇到了多个教程和做事方式。有些人建议尽可能使用智能指针,其他人则发誓the devils tool

我目前的主要内容如下:

//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");
}

这是我得到的输出:

enter image description here

我想我的问题仍然存在,有人能举例说明它应该被视为优秀的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 ++吗?

1 个答案:

答案 0 :(得分:5)

我认为关于C ++ 最重要的事情之一,特别是如果你来自Java / C#background

默认情况下,对象是值类型,而不是引用类型!

您可以简单地编写完整的代码:

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指针基本上是“观察者”的东西,他们可以查看“没有”(或null)。总是认为你是否可以用C ++引用替换C指针。如果你可以做,它。
  3. 如果出于某种原因需要动态内存分配,请使用std::unique_ptr,例如动态多态。请记住,C ++主要在模板上作为静态多态,而不是Java样式继承+覆盖技术。
  4. 很少使用std::shared_ptr,并且只有在您确定有许多所有者时才会使用,例如在不同线程上引用的对象。在极端情况下应使用std::shared_ptr。始终复制共享指针。永远不要通过引用传递共享指针。
  5. 无论如何,newnew[]deletedelete[]几乎已被弃用。只有图书馆作者才能在非常极端的情况下使用它们。 std::make_应该是在堆上分配对象的唯一方法,以及std::vectorstd::list等STL容器。