'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>
的新构造函数。使用 using
,Vector3<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
未初始化。
所以最近我一直在用 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>
的新构造函数。使用 using
,Vector3<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
未初始化。