Classes control object initialization by defining one or more special member functions known as constructors.
- 跟func很像,但是不能const;且由于名字和class一样,所以遵循overloaded的规则;而且它只做一件事,没有返回值
- A constructor is run whenever an object of a class type is created.
- constructors have a (possibly empty) parameter list and a (possibly empty) function body.
struct Sales_data {
// constructors added
Sales_data() = default;
Sales_data(const std::string &s): bookNo(s) { }
Sales_data(const std::string &s, unsigned n, double p):
bookNo(s), units_sold(n), revenue(p * n) { }
Sales_data(std::istream &);
// other members as before
std::string isbn() const { return bookNo; }
Sales_data& combine(const Sales_data&);
double avg_price() const;
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
Sales_data::Sales_data(std::istream &is) {
read(is, *this); // read will read a transaction from is into this object
}
Default Constructor
其实就是当没给 initializers 的时候,所 initial 的,就是 default 的 (⚠️ 注意,没给 initializers 所对应的 default 方案有多种:)
-
当class中没有明确定义任何constructors,那就会隐含一个默认的构造函数。也就是说,如果有其他的构造函数,就不会隐含一个默认的,就好比上面例子的第一句话
-
使用
=defalut
Do Not Rely on Default Constructor
default其实跟正常的define没差,如果没给一个in-class init,那该undefined就是undefined;也因此如果内置一个class,但是这个class也很糟糕,那么也默认不出来(就是C++中不会无中生有)
- A constructor that supplies default arguments for all its parameters also defines the default constructor.
(这里的DA应该说的就是我没给任何arguments但是依旧有东西进去了。对应有1. funcs中的默认参数 2. delegating constructor。巧妙利用这一条可以将default和别的合二为一)
我想也是因为有最后一条规则,才会出现第一条的高光部分吧。
Default Argument is Not the Best code-style
不是说提供default arguments就好,有的时候我们希望两个mem能成对出现,只有不提供,才会在compile-time的时候提示用户输入;如果提供了的话,那用户不输入的时候,就会使用默认值了,这不是我们想要的
Constructor Initializer List
在理解这个东西之前,先好好想想 initial 跟 assign 的区别。没错,区别就在这,这也是为什么constructor initializer list的书写规范是这样的
写在block中的,是作为 assign 的,而 initial 是发生在这个obj be created的时候(aka初始化发生在赋值之前,进入block的时候,initial就已经完成了)。这也是为什么书写规范是这样的:在block之前,在initializers之后。
When initial and assign matters
通常情况下,混用这两个没什么问题,但是对于const和reference这种一定要init的type;或者那些没有default的class (因为init发生在assign之前),这两个就要区分开了
class ConstRef { public: ConstRef(int ii); private: int i; const int ci; int &ri; }; // error: ci and ri must be initialized ConstRef::ConstRef(int ii) { // assignments: i = ii; // ok ci = ii; ci = ii; // error: cannot assign to a const ri = i; // error: ri was never initialized }
Careless Mistake When Using Default Constructor
Sales_data obj(); // oops! declares a function, not an object Sales_data obj2; // ok: obj2 is an object, not a function
第一句话就说了,是一个特殊的函数。确实,这个东西在block内做的事情不是init. 【猜想】有这个block只是为了让所谓的“initial”更加的灵活,做更多的事
Order of Member Initialization
constructor中的para顺序并不影响初始化的顺序,影响的是这些member在class中的定义的先后。
最好是按照mem的声明顺序来给para的顺序,并且最好不要用mem来init mem(因为有可能会让一个mem用上另一个undefined mem)
Delegating Constructors
class Sales_data {
public:
// nondelegating constructor initializes members from corresponding arguments
Sales_data(std::string s, unsigned cnt, double price):
bookNo(s), units_sold(cnt), revenue(cnt * price) { }
// remaining constructors all delegate to another constructor
Sales_data(): Sales_data("", 0, 0) {}
Sales_data(std::string s): Sales_data(s, 0,0) {}
Sales_data(std::istream &is): Sales_data() { read(is, * this); }
// other members as before
};
允许一个constructor在自己的class中找另一个constructor来帮忙init(我想目的应该还是那纯真的简洁)
在原本 initializer list 处直接调用别的 constructor,可以直接给定 arguments(delegate可以嵌套)
控制流:delegting接收参数,调用delegated-to的全部包括function body,回到delegating的body
Class-Type Conversion
单参 constructor 定义了一个 implicit type conversion 就是当需要隐式类型转换的时候,单参构造函数会被调用
string null_book = "9-999-99999-9";
// constructs a temporary Sales_data object
// with units_sold and revenue equal to 0 and bookNo equal to null_book
item.combine(null_book);
/* ==^== */
// assignment create a type conversion context
Sales_data book = null_book;
⚠️ Only One Class-Type Conversion Is Allowed
有时这样的 conversion 也不是我们想要的,因为这样出来的 obj 都是 temporary,想再用就无了,所以我们可以给构造函数加上 explicit
因为是 explicit
不把 static_cast< >
叫出来玩玩多不礼貌:
item.combine(static_cast<Sales_data>(cin));