C+中的父子类关系+;带指针 我应该如何在C++中实现父子关系?我希望父母一次只能有一个活跃的孩子,但我也希望孩子“知道”自己的父母是谁 子类是抽象的,因此只能在父类中声明指向它的指针 Child有一个指向其父的受保护指针,但我不希望它拥有该指针,我不希望实现者删除父指针指向的内存 指针重新分配呢?父级的活动子级可以更改。此时,我将删除当前子指针后面的内存,并将其设置为新的子指针

C+中的父子类关系+;带指针 我应该如何在C++中实现父子关系?我希望父母一次只能有一个活跃的孩子,但我也希望孩子“知道”自己的父母是谁 子类是抽象的,因此只能在父类中声明指向它的指针 Child有一个指向其父的受保护指针,但我不希望它拥有该指针,我不希望实现者删除父指针指向的内存 指针重新分配呢?父级的活动子级可以更改。此时,我将删除当前子指针后面的内存,并将其设置为新的子指针,c++,pointers,C++,Pointers,我希望父项拥有当前子项指针,并在更改时释放它,但我不希望子项拥有其父项指针 我应该使用智能指针吗?我来自C#背景,在那里很容易做到这一点。有什么我没听说过的模式吗 这是我目前掌握的代码: #pragma once class Child; class Parent { private: Child* current_child = nullptr; public: void ChangeChild(Child* child_ptr); }; // Parent.cpp vo

我希望
父项
拥有当前
子项
指针,并在更改时释放它,但我不希望
子项
拥有其
父项
指针

我应该使用智能指针吗?我来自C#背景,在那里很容易做到这一点。有什么我没听说过的模式吗

这是我目前掌握的代码:

#pragma once

class Child;

class Parent
{
private:
    Child* current_child = nullptr;
public:
    void ChangeChild(Child* child_ptr);
};

// Parent.cpp
void Parent::ChangeChild(Child* child_ptr)
{
    if (child_ptr == nullptr)
    {
        // throw
    }
    
    delete current_child;

    current_child = child_ptr;
}
子类
抽象类:

#pragma once

#include "Parent.h"

class Child
{
protected:
    Parent* parent;
public:
    Child(Parent* parent_ptr)
    {
        parent = parent_ptr;
    }

    virtual void Do() = 0;
};

我不确定我是否得到了您设计中的所有约束,但以下是一个开始:

#包括
#包括
#包括
类节点{
公众:
///@brief一个没有父节点和子节点的节点。
显式Node()noexcept:m_child{nullptr},m_parent{nullptr}{
std::cout child()->set_child(std::make_unique());
//不变量
断言(root->child()->child()->parent()==root->child());
}
可能的产出:

$ clang++ example.cpp -std=c++2a -Wall -Wextra -Werror -fsanitize=address
$ ./a.out
Node(0x6020000000d0)
Node(0x6020000000f0)
Node(0x602000000110)
~Node(0x6020000000f0)
Node(0x602000000130)
~Node(0x6020000000d0)
~Node(0x602000000110)
~Node(0x602000000130)

关于这个话题,我看过的最好的演讲可能是赫伯·萨特(Herb Sutter)的演讲。看吧。他详细介绍了何时使用不同的指针(
共享指针
唯一指针
弱指针
,“原始”指针和引用)。他还讨论了与深度递归设计相关的递归销毁问题。

假设父对象被销毁时,每个
子对象都将被销毁,您只需使用
std::unique_ptr
。为方便起见,您可以提供一个通过po的
set_Child
函数您可以将其扩展为可变模板,这样它也可以传递任意数量的参数-如果对您有用的话,谷歌“完美转发函数”

std::shared_ptr
std::weak_ptr
如果允许
Child
派生对象比其父对象长寿,并且可能会回调到
Child
函数,以了解父对象是否仍然存在。)

代码也可在

#包括
#包括
结构父级;
结构子对象{
虚拟~Child(){}
子(父*p_父):p_父{p_父}{}
虚int f()常量{return 0;}
家长*p_家长u;
};
结构父级
{
std::唯一的\u ptr p\u子\uu;
//可选便利功能-为您注入“this”+
//如果p_child_uu是私有的,则提供更好的封装
模板
void set_child(){
p_child_uu=std::使_唯一(此);
}
Child&Child(){return*p_Child}
};
结构子对象1:子对象{
使用Child::Child;
int f()常量重写{return 1;}
};
int main()
{
亲本p;
p、 p_child=std::make_unique(&p);//直接分配

Std::Cout?这里没有第一个头文件问题的循环引用吗?你是对的!但是这可能是用前向声明来固定的。我真的不明白。通过声明,子类知道它的父母。当创建子实例时,父类“填充”也被包含进来。考虑使用智能指针。(例如,unique_ptr或弱_ptr)。关于您的问题:在您的设计中使用智能指针肯定是更好的选择,但您需要注意所有权语义,以及弱引用(例如,父指针).Child的唯一指针很少能满足要求,因为您可能希望将它们与客户端应用程序共享,并跟踪这些引用。我的做法是依赖唯一所有权,直到证明需要共享为止。当然,这是一个很好的做法。我只是从此类构造的经验谈起,即你将以
std::shared_ptr
作为正确的设计结束。
Parent
应该可以有多个
s这一点我也弄错了吗?家长总是只有一个孩子。所有以前的孩子都可以删除。@Escualo这是我避免回答不清楚的问题的一个原因。孩子不应该被排除在外活在父对象中(在本例中)@JohnyP.太简单了-只需要呈现的代码。仅供参考/-有人讨论过在语言中添加
std::nonowning_ptr
,这会使预期的语义比原始指针更明显,但现在原始指针应该只用于非所有权语义,如
unique_ptr
shared\ptr
弱\u ptr
覆盖了99%的其他情况,如果您有一些定制需求,您可能应该编写您自己的智能指针来覆盖它。是什么阻止某人只做
删除我的原始\u ptr;
并删除它背后的内存?@JohnyP.:封装;这是一个问题-
p\u父对象将如果在客户端使用不可信的较大代码体中使用,通常会使其成为
私有的
。@JohnyP。类始终可以访问自己的私有成员,并提供
父和父(){return*p_parent}
常量parent&parent()常量{
#include <iostream>
#include <memory>

struct Parent;

struct Child {
    virtual ~Child() { }
    Child(Parent* p_parent) : p_parent_{p_parent} { }
    virtual int f() const { return 0; }
    Parent* p_parent_;
};

struct Parent
{
    std::unique_ptr<Child> p_child_;
    
    // optional convenience function - injects "this" for you +
    // provides better encapsulation if p_child_ is made private
    template <typename ChildT>
    void set_child() {
        p_child_ = std::make_unique<ChildT>(this);
    }
    
    Child& child() { return *p_child_; }
};

struct Child__1 : Child {
    using Child::Child;
    int f() const override { return 1; }
};

int main()
{
    Parent p;
    p.p_child_ = std::make_unique<Child>(&p); // direct assign
    std::cout << p.child().f() << " should be 0\n";
    p.p_child_ = std::make_unique<Child__1>(&p);
    std::cout << p.child().f() << " should b1 1\n";
    p.set_child<Child>(); // using convenience function...
    std::cout << p.child().f() << " should be 0\n";
}