一、什么是构造函数
类的一个或几个特殊的成员函数,用来控制其对象(非static)数据成员的初始化过程,即,定义了类对象的初始化方式。
构造函数是c++类中非常基础且重要的组成部分。构造函数的名字和类名相同,没有返回类型,有(可能为空)参数列表和函数体。
1.1 默认构造函数
如果没有显示地定义任何构造函数,编译器会为我们隐式地定义一个默认构造函数。
默认构造函数初始化规则:
- 如果存在类内的初始值,用它来初始化成员
- 否则,默认初始化。
1.2 构造函数为什么没有返回值?
C++标准说构造函数是特殊的成员函数,不可以返回任意值。
1.3 构造函数为什么不能声明为const?
原因:在创建类的一个const对象时,直到构造函数完成初始化过程,对象才能真正取得其const属性。因此,构造函数在const对象的构造过程中可以向其写值。《c++ primer 中文第五版》 p235
1.4 类内初始值问题
默认构造函数或构造函数中没有对成员变量赋于类内初始值时,类对象成员变量的值是未定义的(类似仅定义而未初始化的局部变量),这种场景是不安全的,可能引发异常。
class Person {
public:
Person() {} // 1 函数体为空
Person() = default; // 2 函数体为默认
// 3 没有显式定义构造函数
private:
int age;
string name;
};
int main() {
Person p;
return 0;
}
上述三种构造方式均产生下图结果(IDE vs2019)
1.5 成员初始化列表与在构造函数体中对成员赋值的区别?
- 效率差异,初始化列表效率更高
When you initialize fields via Member initializer list the constructors will be called once and the object will be constructed and initialized in one operation.
If you use assignment then the fields will be first initialized with default constructors and then reassigned (via assignment operator) with actual values.
As you see there is an additional overhead of creation & assignment in the latter, which might be considerable for user defined classes.
Cost of Member Initialization = Object Construction
Cost of Member Assignment = Object Construction + Assignment
The latter is actually equivalent to:
Foo(int num) : bar() {bar = num;}
While the former is equivalent to just:
Foo(int num): bar(num){}
For an inbuilt (your code example) or POD class members there is no practical overhead.
- 某些场景必须使用初始值列表
When do you HAVE TO use Member Initializer list?
You will have(rather forced) to use a Member Initializer list if:
- Your class has a reference member
- Your class has a non static const member or
- Your class member doesn’t have a default constructor or
- For initialization of base class members or
- When constructor’s parameter name is same as data member(this is not really a MUST)
class MyClass {
public:
// Reference member, has to be Initialized in Member Initializer List
int &i;
int b;
// Non static const member, must be Initialized in Member Initializer List
const int k;
// Constructor’s parameter name b is same as class data member
// Other way is to use this->b to refer to data member
MyClass(int a, int b, int c) : i(a), b(b), k(c) {
// Without Member Initializer
// this->b = b;
}
};
class MyClass2 : public MyClass {
public:
int p;
int q;
MyClass2(int x, int y, int z, int l, int m) : MyClass(x, y, z), p(l), q(m) {}
};
int main() {
int x = 10;
int y = 20;
int z = 30;
MyClass obj(x, y, z);
int l = 40;
int m = 50;
MyClass2 obj2(x, y, z, l, m);
return 0;
}
MyClass2 doesn’t have a default constructor so it has to be initialized through member initializer list.
- Base class MyClass does not have a default constructor, So to initialize its member one will need to use Member Initializer List.
从C++11开始,已经支持非静态成员(就地)初始化。
初始值列表初始化顺序只与类中定义顺序有关。
Important points to Note while using Member Initializer Lists:
Class Member variables are always initialized in the order in which they are declared in the class.
They are not initialized in the order in which they are specified in the Member Initializer List.
In short, Member initialization list does not determine the order of initialization.
Given the above it is always a good practice to maintain the same order of members for Member initialization as the order in which they are declared in the class definition. This is because compilers do not warn if the two orders are different but a relatively new user might confuse member Initializer list as the order of initialization and write some code dependent on that.
1.6 默认构造函数中"=defaullt"与"{}"区别?
For example in a class A, what is the difference between using this default constructor A(){}; or A() = default;
回答: https://stackoverflow.com/questions/23698203/c-default-constructor-syntax?noredirect=1&lq=1
1.7 委托构造函数的使用.
explict 抑制构造函数定义的隐式转换。它只对一个实参的构造函数有效。同时,只能以直接初始化的方式使用。
Sales_data item1(null_book); // 正确,直接初始化
Sales_data item2 = null_book; // 错误,拷贝初始化
1.8 构造函数没有返回值,如何判断对象创建是否成功?
在 C++ 里,构造函数抛出异常是判断对象创建是否成功的常用手段。要是构造函数在执行期间抛出异常,那么对象的创建就会失败,并且不会调用析构函数。
在下面示例中,若传入的value为负数,构造函数就会抛出异常,这意味着对象创建失败
#include <iostream>
#include <stdexcept>
class MyClass {
public:
MyClass(int value) {
if (value < 0) {
throw std::invalid_argument("Value must be non-negative");
}
// 正常初始化操作
data = value;
}
private:
int data;
};
int main() {
try {
MyClass obj(10);
std::cout << "Object created successfully." << std::endl;
} catch (const std::exception& e) {
std::cerr << "Failed to create object: " << e.what() << std::endl;
}
return 0;
}