如何使用子对象的成员初始化父对象

How to initialize parent using member of child?

本文关键字:对象 初始化 成员 何使用      更新时间:2023-10-16

我有一个需要初始化的类:

//Parent.h
class Parent {
public:
    Parent(Image image);
private:
    const Image parentImage;
}
//Parent.cpp
Parent::Parent(Image image) : parentImage(image) {}
//Child.h
#import "Parent.h"
class Child : public Parent {
public:
    Child(int c);
private:
    Camera childCamera;
}
//Child.cpp
Child::Child(int c) : Parent(this->childCamera.getImage()), childCamera(c) {}

Camera需要初始化,然后才能从中检索图像。Parent存储来自相机的图像,这是const,子存储Camera。如何创建Child ?我不能改变Parent,因为有其他子类以其他方式初始化Parent。我可以更改Child,但Camera不能复制,Child确实需要存储Camera

编辑:我可以添加构造函数到Parent,如果这是最干净的解决方案。

你不能这么做。

根据标准(12.6.2/10),初始化顺序为:

— (...)

-然后,直接基类在声明中初始化按照它们在基本指定符列表中出现的顺序排序(不管它们的

-则非静态数据成员为按照在类定义中声明的顺序初始化(同样,不考虑内存初始化式的顺序)。

——最后,执行构造函数体的复合语句。

所以在Child构造函数的初始化列表中,对于Parent初始化器,不能使用任何依赖于Child成员变量的东西,因为它们在那一刻被初始化了。

但是,您可以使用Child构造函数的参数,例如:

Child::Child(int c) : Parent(c.getImage()), childCamera(c) {}
当然,如果Camera对象的每个副本都返回与原始对象相同的图像,则

另一种选择是使用来自辅助类的多重继承(使用上面标准引号的第二个破折号)。但这是更棘手的,无论如何也需要相机对象的副本。

考虑到您列出的所有设计约束,一个合理的解决方案是在Child的外部构造一个Camera,然后将其传递给Child的构造函数。Child将获得它的所有权。

所以类可以写成:

class Child : public Parent {
public:
    Child(Camera* camera);
private:
    std::unique_ptr<Camera> childCamera;
}
//Child.cpp
Child::Child(Camera* camera) 
    : Parent(camera->getImage()), childCamera(camera) 
{}

然后用作:

Camera* camera = new Camera;
Child child(camera);

不行。你不能使用任何依赖于Child的元素来代替Parent

您可以使用Base From Member习语来实现这一点。基本上,你创建一个中间的持有者类/结构来保存曾经在基类中的变量,然后你首先从持有者类继承子类,然后从基类继承子类。使用该方法,您可以初始化父

当您运行下面的代码时,您将看到在有问题的情况下,基类在初始化时没有获得预期的值。但在解决方案中,它是按预期初始化的。让我知道这是否适合你。

#include "fmt/core.h"
struct TempStruct
{
    explicit TempStruct(int temp) : tempI_(temp)
    {
        fmt::print("TempStruct {} n", tempI_);
    }
    int tempI_{33};
};
class BaseClass
{
  public:
    explicit BaseClass(TempStruct* pTemp)
    {
        fmt::print("BaseClass tempstruct {}n", pTemp->tempI_);
    }
};
class ChildClass : public BaseClass
{
  public:
    // Below code doesn't work as expected. BaseClass initilizers first and then
    // TempStruct. So BaseClass doesn't get TempStruct with 5. Field
    // 'tempStruct_' will be initialized after base 'BaseClass' [-Wreorder-ctor]
    ChildClass() : tempStruct_{5}, BaseClass(&tempStruct_)
    {
    }
  private:
    // Member initialize tempStruct_ to 10;
    TempStruct tempStruct_{10};
};
// Solution
struct TempStructHolder
{
    TempStructHolder(int i) : tempStruct_{i}
    {
    }
    TempStruct tempStruct_{0};
};
class SolutionChildClass : public TempStructHolder, public BaseClass
{
  public:
    SolutionChildClass() : TempStructHolder(39), BaseClass(&tempStruct_)
    {
    }
};
int main()
{
    fmt::print("Problem n");
    [[maybe_unused]] const ChildClass c;
    fmt::print("Solutin n");
    [[maybe_unused]] const SolutionChildClass sc;
}