目录
1、Delegation Constructor(代理构造)
1. What is delegating constructor? (什么是代理构造/委托构造)
代理构造:一个构造函数可以调用另外的构造函数
在代理构造的时候,被委托的要放在委托的构造函数的初始化列表的位置,不能放在在括号里面。
class A{
public:
A(): A(0){}
A(int i): A(i, 0){}
A(int i, int j) {
num1=i;
num2=j;
average=(num1+num2)/2;
}
private:
int num1;
int num2;
int average;
};
上面例子中,构造函数的调用次序:
A() ->A(int) ->A(int, int)
在这个过程中,形成了所谓的委托构造量,我们最怕的就是出现环形的委托构造。
2. Avoiding recursive calls of target constructors (避免递归调用目标ctor)
递归调用:函数调用其本身
递归调用实际上就是环形调用。
class A{
public:
A(): A(0){}
A(int i): A(i, 0){}
A(int i, int j): A(){}
private:
int num1;
int num2;
int average;
};
调用次序:
A() -> A(int) ->A(int, int) ->A()
3. 委托构造的好处
让程序员少写代码,使逻辑更加清晰。
执行程序时, 会先执行 一个参数的构造函数, 然后再执行两个参数的构造函数。
大大减少了代码量。
2、不可变对象和类
不可变对象:对象创建后,其内容不可改变,除非通过成员拷贝
不可变类:不可变对象所属的类
1、如何让类成为“不可变类”
1、所有数据域均设置为“私有”属性
2、没有更改器函数
3、也没有能够返回可变数据域对象的引用或指针的访问器
如下:
2、特殊情况:指针成员
如果只有下面两个条件,是构成不了不可变类的。
1、所有数据域均设置为“私有”属性
2、没有更改器函数
如果getter函数返回指向成员的指针,或者getter函数返回成员的引用。那么通过getter函数也能够修改类成员。
如下:指针指向了类的私有成员,然后我们修改对象的私有数据成员,让对象不成为不可变类。
所以才要加入第三个条件:没有能够返回可变数据域对象的引用或指针的访问器
3、例子
#include<iostream>
#include<string>
using namespace std;
class Date {
private:
int year = 2019, month = 1, day = 1;
public:
int getYear() { return year; }
int getMonth() { return month; }
int getDay() { return day; }
void setYear(int y) { year = y; }
void setMonth(int m) { month = m; }
void setDay(int d) { day = d; }
Date() = default;
Date(int y, int m, int d) :year(y), month(m), day(d) {}
std::string toString() {
return (std::to_string(year) + '-' + std::to_string(month) + '-' + std::to_string(day));
}
};
enum class Gender {
male,
female,
};
class Employee {
private:
std::string name;
Gender gender;
Date birthday;
public:
void setName(std::string name) { this->name = name; }
void setGender(Gender gender) { this->gender = gender; }
void setBirthday(Date birthday) { this->birthday = birthday; }
std::string getName() { return name; }
Gender getGender() { return gender; }
Date* getBirthday() { return &birthday; }
std::string toString()
{
return (name +( (gender == Gender::male ? std::string(" male ") : std::string(" female ") )+ birthday.toString()));
}
//带参构造函数
Employee(std::string name,Gender gender,Date birthday):name{name},gender{gender},birthday{birthday}{}
//默认构造函数
Employee():Employee("Alan",Gender::male,Date(2000,4,1)){}
};
//创建Employee对象,然后修改其生日
int main()
{
Employee e;
//1:setter
e.setBirthday(Date(1999,1,1));
std::cout << e.toString() << std::endl;
//2:getter
e.getBirthday()->setYear(1998);
std::cout << e.toString() << std::endl;
return 0;
}
4、不可变对象的“不可变”特征在软件开发中有什么用? 它和thread-safe有什么关系?
将一些需要共享和协同开发的内容设为不可变对象可以防止内容被他人修改。起到类似const的作用。
详细可以观看这篇文章:
线程安全ThreadSafe
3、不可变对象和类
1、声明或定义静态成员
在类定义中,关键字 static 声明 不绑定到类实例的成员( 该成员无需创建实例化对象即可访问,可以直接用类去访问)在类的内部声明
静态成员的定义是很复杂的。
静态数据成员定义的规则:
(1) 声明为“constexpr”类型的静态数据成员必须 在类中声明 并初始化。自C++17 起,可不在类外定义
(2) 声明为==“inline”(C++17 起) 或者 “const int” == 类型的静态数据成员可以 在类中声明 并初始化;
(3) 其它须在类外 定义并初始化,且不带static 关键字
静态数据成员的定义规则复杂,在类外定义,大部分情况下不会出错
2、静态成员
静态数据成员具有静态存储期(static storage duration)或者C++11线程存储期特性。
静态存储期
对象的存储在程序开始时分配,而在程序结束时解回收。
(1) Only one instance of the object exists ( 只存在对象的一个实例)
(2) 静态存储器对象未明确初始化时会被自动“零初始化(Zero-Initialization)”
3、实例成员和静态成员
一旦实例化了Square(创建了Square的对象),每个对象中都有各自的side成员。这个side成员就叫做实例成员。
而numberOfObjects只存在一个,是由所有的Square对象共享的,叫做静态成员。
class Square {
private:
double side;
static int numberOfObjects;
// ...
public:
//代理构造,无参构造函数调用有参构造函数
Square():Square(1.0){
}
Square(double side){
this->side = side;
numberOfObjects++;
}
// ...
};
//定义静态数据成员,会被零初始化
int Square::numberOfObjects;
int main() {
Square s1{}, s2{5.0};
}
调用一次构造函数,numberOfObjects就会+1,所以numberOfObjects可以统计成员数量