C++ Primer05其他形式构造函数与静态类型

新一年的第一天,今年注定会是不平凡的一年。加油了!

今天我们将会看到:构造函数初始化列表、委托构造函数、隐式类类型转换、以及类的静态成员。

1.构造函数初始化列表

我们在声明构造函数时,可以这样:

	Sales_data(const string& s, unsigned n, double p)
	{
		bookNo = s;
		units_sold = n;
		revenue = p;
	}

但是我们也可以这样:

Sales_data(const string& s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(p* n) {}

这两种方法有区别吗?
有的!第一种方法,数据成员将在构造函数体之前执行默认的初始化,然后再在函数体部分对其进行赋值。而第二种方法是直接初始化了这些数据成员。是用第一种还是第二种关乎底层的效率。建议使用构造函数初始值。

你可能会说,这好像没什么不同。不是的,有时构造函数的初始值是必不可少的。 比如说:对于const、引用和未提供默认构造函数的类成员,它们的成员初始化列表是必不可少的。

2.成员初始化的顺序

构造函数初始值列表只说明用于初始化成员的值,但并不代表初始化的顺序就是这样的。初始化的顺序与数据成员定义的顺序是一致的。 下面就是一个错误的例子:

class X{
	int i;
	int j;
public:
	X(int val):j(val),i(j) {}
}

这种情况是未定义的。因为初始化的顺序是先初始化 i,在初始化 j 。

3.默认实参和默认构造函数

Sales_data(const string& s = "") :bookNo(s) {};

如果在我们的构造函数中定义这样一句话,那其实相当于我们也同样定义了默认构造函数。因为我们不给对象传实参也也同样可以声明一个对象。
如果一个构造函数为所有参数提供了默认实参,则它实际上也定义了默认构造函数。

4.委托构造函数

一个委托构造函数使用它所属类的其他构造函数来执行它的初始化过程。

	Sales_data(const string& s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(p* n) {}
	Sales_data() :Sales_data("", 0, 0) {}
	Sales_data(const string& s) :Sales_data(s, 0, 0) {}
	//explicit Sales_data(istream& in) :Sales_data() { read(in, *this); }
	Sales_data(istream& in) :Sales_data() { read(in, *this); }

在这个例子中,除了第一个构造函数外,其他构造函数均为委托构造函数,比如第二个构造函数,它把它的工作委托给了上面那个构造函数。

执行顺序:执行受委托的构造函数的初始化列表以及代码,然后控制权回到委托者的函数体。

5.隐式的类类型转换

如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,有时我们把这种构造函数称为转换构造函数

在我们编写的类中

Sales_data(const string& s) :Sales_data(s, 0, 0) {}
Sales_data(istream& in) :Sales_data() { read(in, *this); }

是两个转换构造函数。

不信你看:

	string str = "999";
	Sales_data a("999", 10, 10);
	a.combine(str);
	print(cout, a);

如果我们这样用是不会报错的。因为进行了隐式类型转换。

当然,这种隐式类型转换只允许一步转换。比如这样就不行

item.combine("999")

因为这需要两步转换,先将"999"转换为string类型,然后再将string转换为类类型。

当然,我们也可以抑制这种转换,让这种转换不被允许发生。方法就是:
将构造函数声明为 explicit
在这里插入图片描述在这里插入图片描述
当我们将构造函数声明为explicit 你会发现这里会报错。

需要注意的是:只能在类内声明构造函数时使用explicit,在类外部定义时不应重复。

6.类的静态成员

有时候,类需要它的一些成员与类本身直接相关,而不是与类创建的各个对象直接相关。静态成员就是用来干这个的。

静态成员声明方法很简单:在声明前加上static关键字就好,静态成员同样可以有访问控制。

下面是一个账户类Account.h

#pragma once
#include<iostream>
#include<string>
using namespace std;
class Account
{
public:
	Account(string oname, double oam) :owner(oname), amount(oam) {};
	void calculate();
	double getAmount();
	static double rate();
	static void rate(double);
private:
	string owner;
	double amount;
	static double interestRate;
	static double initRate();
};

Account.cpp

#include"Account.h"
#include<iostream>
using namespace std;

double Account::rate()
{
	return interestRate;
}

double Account::getAmount()
{
	return amount;
}

void Account::rate(double newRate)
{
	interestRate = newRate;
}

double Account::initRate()
{
	return 1.1;
}

double Account::interestRate = initRate();

可以看到类中包含了一个静态数据成员:利率。

类的静态成员存在于任何对象之外,对象中不包含任何与静态数据成员有关的数据

静态成员函数不包括this指针,因此静态成员函数一般用来访问静态数据成员。

类的静态成员的 使用:可以直接使用作用域标识符,当然也可使用对象来访问。

static关键字只能在类内使用,在类外不应重复使用。

静态数据成员的定义初始化:静态数据成员不属于类的任何一个对象,它们不是由类的构造函数初始化的。将静态数据成员的定义与其他非内联函数的定义放在一起是最好的办法。 它们一旦被定义完成,一直存在于程序的整个生命周期中。

下面来使用一下:

#include"Account.h"
#include<iostream>
using namespace std;
int main()
{
	Account cn("cuinan", 10000);
	Account dd("dd", 20000);
	cout << Account::rate()<<endl;
	cout << cn.rate()<<endl;
	cout << dd.rate() << endl;
	Account::rate(1.2);
	cout << Account::rate() << endl;
	cout << cn.rate() << endl;
	cout << dd.rate() << endl;
}

在这里插入图片描述
可以看到,我们直接使用类名来访问静态成员与我们通过对象来访问静态成员得到的结果一样。这句话很经典建议记住:静态成员是属于整个类的,而不是属于某一个对象的。类的静态成员独立于任何对象之外,对象中不包含任何与静态数据成员有关的信息。静态数据成员被所有的对象所共享。

到这里,类的基础部分就算是介绍完了。懂了这些也只是万里长征的第一步。后面类的特性更加复杂。加油,搞懂它!奥利给!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值