初始化列表、静态成员、友元
一:构造函数函数初始化列表初始化:
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
类中包含以下成员,必须放在初始化列表位置进行初始化:
- 引用成员变量
- const成员变量
- 自定义类型成员(该类没有默认构造函数) 默认构造函数就是不用传参可以调用的构造函数有三个:1.全缺省 2.无参的 3.编译器自动生成的
class A
{
public:A(int x = 0) // 默认构造函数 定义Date d的时候可以调动_x = x;A(int x) // 不是默认构造函数 定义Date d的时候调不动,得配合在Date类的初始化的初始化列表调用才可以调得动_x = x;
private:int _x;
}
class Date
{
public:Date(int year, int month, int day):_year(year),_month(month),_day(day),_n(10),_tmp(year),_a(1) //如果A中没有默认构造函数就需要这样子做{}
private:// 不是成员变量的定义,仅仅是成员变量的声名// 这些成员都属于对象,在main中对象被定义出来后才算定义了成员变量int _year;int _month;int _day;int& _tmp; //引用必须在定义的时候(初始化列表)初始化 (const也得在定义的时候初始化)const int _n; //必须在初始化列表初始化,不能在函数体内初始化A _a; //自定义类型定义的时候自动调用它的构造函数
};
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
下述代码private中先定义的_a2,因此初始化先初始化a2,再初始化_a1,所以选D。1 随机值
class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() {cout<<_a1<<" "<<_a2<<endl;}
private:int _a2;int _a1;
}
int main() {A aa(1);aa.Print();
}
单参数的构造函数支持隐式类型转换。
class A
{
public:A(int a):_a(a){}
private:int _a;
};
class B
{
public:explicit A(int b) //加上这个关键字不支持类型转换:_b(b){}
private:int _b;
};
int main()
{ int i = 0;double j = i; // 隐式类型转换A a1 = 2; // 将int类型2转换为 A 类型 调用构造函数构造B b1 = 2; // 不能编译过去A(2); // 构造匿名对象 生命周期只在这一行
}
二:静态成员:
实现一个类,计算中程序中创建出了多少个类对象。
在类中定义静态成员,然后在构造和拷贝构造函数中对静态成员变量进行++,每调用一次就会自己++一次。类中的静态成员变量是声名,不在构造函数初始化,需要在类外进行初始化定义。
静态成员函数和普通的成员函数区别:没有this指针,不能访问非静态成员。
- 静态成员为所有类对象所共享,不属于某个具体的实例,它在静态区
- 静态成员变量必须在类外定义,定义时不添加static关键字
- 类静态成员即可用类名::静态成员或者对象.静态成员来访问 就是A()::GetN()、a1.GetN()、A::GetN()
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值
静态成员函数可以调用非静态成员函数吗?不可以 在GetN中不能调用f,因为GetN没有this指针
非静态成员函数可以调用类的静态成员函数吗?可以,f可以调用GetN。
class A
{
public:A(){_n++;}A(const A& a){_n++;}//int GetN()//{// return _n;//}void f(){}// 静态成员函数和普通的成员函数区别:没有this指针,不能访问非静态成员。static int GetN(){}
private:// 声名 不在构造函数初始化static int _n; // 存在静态区 属于整个类 属于类的所有对象 生命周期是全局的
};
int A::_n = 0; // 在这里初始化 第一次可以初始化
int main()
{A a;cout << sizeof(A) << endl; // 1cout << sizeof(a) << endl; // 1cout << a.GetN() << endl; // 可以知道创建了多少次对象return 0;
}
题目:求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。数据范围: 0<n≤200 进阶: 空间复杂度 O(1),时间复杂度 O(n)
解:创建一个Add类,n是多少就创建大小为n的Add类型的数组,这样在Add的构造函数定义++,对static类型的数据进行++,就可以完成求和。Init函数是为了防止在oj题目中多接口去测试而导致_i和_ret没有在下次调用初始化的问题。
class Add{
public:Add(){_ret += _i;_i++;}static int Get(){return _ret;}static void Init(){_i = 1;_ret = 0;}
private:static int _i;static int _ret;
};
int Add::_i = 1;
int Add::_ret = 0;
class Solution {
public:int Sum_Solution(int n) {Add::Init();Add arr[n]; //变长数组return Add::Get();}
};
题目:根据输入的日期,计算是这一年的第几天。保证年份为4位数且日期合法。
进阶:时间复杂度:O(n),空间复杂度:O(1)
输入描述:
输入一行,每行空格分割,分别是年,月,日
输出描述:
输出是这一年的第几天
eg:
输入:2012 12 31
输出:366
#include <iostream>
using namespace std;
int GetDay(int year, int month){int dayArray[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};int day = dayArray[month];// 每四年一润但百年不润 但是四百年润if (month == 2 &&((year % 4 == 0 && year % 100 != 0) || (year%400 == 0))){day += 1;}return day;
}
int main() {int _year = 0;int _month = 0;int _day = 0;int ret = 0;cin >> _year >> _month >> _day;while(_month > 0){_month--;ret += GetDay(_year, _month);}cout <<(ret + _day) << endl;
}
cout是ostream类型的对象
cin是istream类型的对象
友元函数:破坏封装的行为。
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰 // const是修饰this指针的,友元函数不属于类函数。
友元函数可以在类定义的任何地方声明,不受类访问限定符限制,可以定义在类中任何位置
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用和原理相同
class Date
{//友元函数的声名 加一个友元的函数声明。friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}// cout << d1; cout就变成左操作数,就把cout给this了。ostream& operator<<(ostream& _cout) // ostream& operator<<(Date* this, ostream& _cout){_cout<<d._year<<"-"<<d._month<<"-"<<d._day;return _cout;}
prvate:int _year;int _month;int _day
};// 友元函数
ostream& operator<<(ostream& _cout, const Date& d) // ostream&的返回值可以支持连续输出
{_cout<<d._year<<"-"<<d._month<<"-"<<d._day;return _cout;
}
istream& operator>>(istream& _cin, Date& d) // 输入需要修改不加const
{_cin>>d._year>>"-">>d._month<<"-">>d._day;return _cin;
}
int main()
{Date d(2017, 12, 24);// cout<<d; // cout就变成左操作数,就把cout给this了,d<<cout; // 这个可以调用成员内函数打印 但写法不太相同 因此直接写成员函数不是很方便 cout << d; // 依靠友元通过类外函数访问成员内变量并打印Date d1(2017, 12, 24); // ostream&的返回值可以支持连续输出Date d2(2017, 12, 24);cout << d1 << d2; // 从左到右结合 cout<<d1返回一个coutreturn 0;
}
友元类: A是B的友元(在B中写friend class A;),A中可以访问B,B不能访问A。
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
友元关系是单向的,不具有交换性。比如Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
友元关系不能传递,如果B是A的友元,C是B的友元,则不能说明C时A的友元。
class Date;
// 前置声明
class Time
{friend class Date; // Date要访问time的内容,把Date定义成time的友元类// 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量public:Time(int hour, int minute, int second): _hour(hour), _minute(minute), _second(second){}
private:int _hour;int _minute;int _second;
};
class Date // Date要访问time的内容,把Date定义成time的友元类
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t.second = second;}
private:int _year;int _month;int _day;Time _t;
};