'using' 父 class 构造函数与在 C++ 中创建新构造函数有什么区别?

What is the difference between 'using' a parent class constructor and creating a new one in c++?

所以最近我一直在用 c++ 开发一个基本的数学库,并决定制作模板 classes 和继承的、专门的 classes,例如 Vector。

我在链接父 class 的默认构造函数时遇到了一些问题,而无需创建新构造函数。 我可以看到 using 关键字还允许我使用我想要的任何基础 class 构造函数,但另一个解决方案(下面的那个)似乎更优雅。

    template<typename type>
    class Vector3D : public Vector<type, 3>
    {
    public:
        /// Telling compiler to use constructors of parent class Vector
        using Vector<type, 3>::Vector;

        Vector3D() : KMath::Vector<type, 3>() {}

正如我所看到的,这两种方法都有效,我很好奇它们之间有什么区别,是否有任何一种被 better/more 广泛使用?

编辑:为了明确起见,我将在此处粘贴父 class:

    template<typename type, unsigned int SIZE>
    class Vector
    {
    protected:
        type vectorTuple[SIZE];

    public:
        explicit Vector()
        {
            unsigned int i;
            for (i = 0; i < SIZE; ++i)
            {
                vectorTuple[i] = 0.0;
            }
        }

        explicit Vector(type arr[SIZE])
        {
            unsigned int i;
            for (i = 0; i < SIZE; ++i)
            {
                vectorTuple[i] = arr[i];
            }
        }
    ...
    };

我的问题不仅在于被调用的默认构造函数,还有另一个构造函数,因为没有 using 关键字编译器会 return 一个错误,如果我尝试过

double arr[3] = {1, 2, 3};
Vector3D<double> vec(arr);

如果没有 using 关键字或在派生的 class 中创建相同类型的构造函数,代码显然无法运行。我在网上的某个地方看到过这个解决方案,只是想看看它是否有任何影响。

如果我们比较使用 using 与手动重新声明相同的构造函数并将它们显式转发到基础 class,那么就结果代码而言,两者实际上是等效的。但是,将代码库视为一个活生生的、不断发展的实体时,存在真正的区别:

它改变了代码所表达的两个class结构之间的逻辑关系。

下面两个说法不是一个意思:

  • “这个 class 与其父级具有相同的构造函数,可能还有更多。”
  • “这个 class 有这些构造函数,其中一些恰好与它的父 现在 相同。”

例如:假设从现在起 6 个月后引入 Vector<size, type> 的新构造函数。使用 usingVector3<type> 将隐式继承该构造函数,而无需任何额外的重构。这是否可取是决定哪种方法合适的区别因素。

编写好的代码不仅仅是让程序以某种方式可靠地运行。它还涉及确保程序的行为 保持 稳定,因为它会在很长一段时间内发展。

TL;DR - 在您的代码段中,您都不需要。您的构造函数是默认构造函数,不要使用参数,您可以轻松删除 using 声明以及显式默认构造函数。

在更一般的情况下,基础构造函数的 using 声明使其在子 class 中可见(否则它被隐式子 class 默认构造函数隐藏)。

当您使用它时,基础 class 构造函数在子 class 中变得可用,并允许通过调用具有匹配签名的基础构造函数之一来构造子 class。

例如:

struct Base {
    Base(int );
};

struct Derived : Base {
    // using Base::Base;
};

void foo() {
    Derived d{1}; // Fails unless using directive is uncommented above
}

当派生的 classes 不需要执行任何自己的初始化,而只是想提供可用的基础构造时,这种方法是可行的。

请注意,using base 的构造函数不会阻止派生的 classes 创建自己的构造函数。例如:

struct Base {
    Base(int );
};

struct Derived : Base {
    using Base::Base;
    Derived(const char* str);
};

在上面的例子中,Dervived d{1}会调用Base:Base(int ),而Derived d{"hello"}会调用Derived::Derived(const char*)

另一方面,当存在与派生 class 构造函数关联的某些逻辑时,可以使用从派生构造函数显式调用基构造函数 - 即没有默认初始化程序的非默认成员初始化、非平凡主体等. 例如:

struct Base {
    Base(int );
};

struct Derived : Base {
    Derived(int x) : Base(x), own(x + 5) { }
    int own;
};

在这种情况下,Derived 构造函数需要做两件事 - 使用 int 参数调用 Base 并初始化 own。所以 using Base 的构造函数不合适,因为它会使 own 未初始化。