先来看一段简单的代码:
#include <iostream>
#include <string>
class Base {
public:
Base() {
std::cout << "Base() is called" << std::endl;
}
Base(int num_) {
std::cout << "Base(int num_) is called" << std::endl;
num = num_;
}
int number() const {
return num;
}
protected:
int num = 10;
};
class Derived : public Base {
public:
Derived(const std::string &address_) {
std::cout << "Derived(const std::string &address_) is called" << std::endl;
address = std::move(address_);
}
private:
std::string address;
};
int main() {
Derived derived("White House");
std::cout << "num is " << derived.number() << std::endl;
return 0;
}
这段代码定义一个父类Base和其子类Derived,并在Base中定义了两个构造函数,Derived中定义了一个构造函数。程序的输出如下:
Derived中的构造函数会首先调用父类的默认构造函数Base()构造父类对象,然后再执行其构造函数剩余的语句,num的输出为默认值10。
现在将Derived构造方式做一些修改:
int main() {
Derived derived;
std::cout << "num is " << derived.number() << std::endl;
return 0;
}
编译出错:
Derived已经自定义了一个构造函数,编译器不会再为Derived合成一个默认构造函数,只能显示用std::string参数构造Derived对象。
在Derived的定义中增加一行代码using Base::Base;并作如下修改:
class Derived : public Base {
using Base::Base;
public:
Derived(const std::string &address_) {
std::cout << "Derived(const std::string &address_) is called" << std::endl;
address = std::move(address_);
}
std::string& get_address() {
return address;
}
private:
std::string address = "Haihu Zhuangyuan";
};
int main() {
Derived derived;
std::cout << "num is " << derived.number() << std::endl;
std::cout << "address is " << derived.get_address() << std::endl;
return 0;
}
此时程序可以编译通过,执行输出如下:
Derived的构造函数没有被调用,反而是父类的默认构造函数可以被调用,似乎子类可以直接调用父类的构造函数构造对象(此时构造父类对象,子类的数据成员使用默认值)。
如果不适用using Base::Base;,要得到上面同样的输出,程序可以这样修改:
class Derived : public Base {
public:
Derived(const std::string &address_) {
std::cout << "Derived(const std::string &address_) is called" << std::endl;
address = std::move(address_);
}
Derived() : Base() {
}
std::string& get_address() {
return address;
}
private:
std::string address = "Haihu Zhuangyuan";
};
上面的程序自定义了一个默认构造函数,在默认构造函数中显示调用父类的默认构造函数,当然也可以这样修改:
class Derived : public Base {
public:
Derived(const std::string &address_) {
std::cout << "Derived(const std::string &address_) is called" << std::endl;
address = std::move(address_);
}
Derived() = default;
std::string& get_address() {
return address;
}
private:
std::string address = "Haihu Zhuangyuan";
};
让编译器合成一个子类的默认构造函数,合成的构造函数会调用父类的默认构造函数。
接着对Derived对象的构造方式做一下修改:
int main() {
Derived derived(80);
std::cout << "num is " << derived.number() << std::endl;
std::cout << "address is " << derived.get_address() << std::endl;
return 0;
}
编译错误:
Derived中缺少对应的构造函数,如果仍然增加using Base::Base;语句呢?
class Derived : public Base {
using Base::Base;
public:
Derived(const std::string &address_) {
std::cout << "Derived(const std::string &address_) is called" << std::endl;
address = std::move(address_);
}
Derived() = default;
std::string& get_address() {
return address;
}
private:
std::string address = "Haihu Zhuangyuan";
};
int main() {
Derived derived(80);
std::cout << "num is " << derived.number() << std::endl;
std::cout << "address is " << derived.get_address() << std::endl;
return 0;
}
此时编译通过,输出如下:
子类可以直接使用父类的构造函数构造对象。
using Base::Base;这行代码的作用是将父类 Base的构造函数引入到子类Derived 的作用域中。这种用法在 C++ 中称为继承构造函数,它允许子类直接使用父类的构造函数,而无需在子类中显式地定义这些构造函数。
如果不使用using Base::Base;,在Derived中只能显示定义拥有和Base构造函数一样参数列表的构造函数,然后再在该构造函数中显示调用父类对应参数列表的构造函数。
使用 using Base::Base; 后,Derived的用户可以直接使用Base 的构造函数,就像这些构造函数是 Derived的一部分一样。
using Base::Base;的作用是让子类 Derived 能够直接使用父类Base的构造函数,从而简化代码并提高复用性。这种技术在模板类中非常有用,因为它可以避免在子类中重复定义父类的构造函数。