目录
一.流插入<<与流提取>>
1.他们能自动识别内置类型的原因:函数重载
2.重载日期类的流插入 cout <<
(1)<<重载函数 不能写在类中,原因:
(2)<<重载函数 写在类外 要搭配 友元
(3)<<重载函数 写在类外不能写在头文件中
那类是在.h文件,类中定义的函数会重定义吗?
(4)为支持连续流插入需要设置返回值(完善版本)
3.流提取>> (和流插入<<搭配)
二.初始化列表
1.定义
2.格式
3.特征
(1)例子1:都初始化
(2)例子2:缺省值和初始化列表
(3)对特征4.的解释
三.explicit关键字
1.用途:
2.构造函数的隐式类型转换
(1)解释拷贝构造 Date d2 = 2022;
(2)解释 const Date& d6=2022 为什么加const
(3)构造函数的隐式类型转换的语法的意义
四.static成员
1.概念
2.使用的例子:
3.静态成员变量和静态成员函数的特征
4.静态成员的访问形式
(1)静态成员变量公有时:
(2)静态成员变量私有时:
静态成员函数特征:
五.C++11 的成员初始化新玩法。
1.C++11支持非静态成员变量在声明时进行初始化赋值
有以下三种写法:
静态成员变量不可以给缺省值原因:
2.静态成员变量的OJ题:JZ64 求1+2+3+...+n
一.流插入<<与流提取>>
1.他们能自动识别内置类型的原因:函数重载
重载了各种内置类型的<<运算符重载函数,所以遇到不同内置类型时,会自动匹配对应的函数重载
cout类型是ostream(out)
cin类型是istream(in)
2.重载日期类的流插入 cout <<
Date.h:
#include
#include
using std::cout;
using std::cin;
using std::endl;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void operator<<(std::ostream& out) //cout的类型是ostream
{
out << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
test.cpp:
void TestDate7()
{
Date d1;
d1.operator<<(cout);
d1 << cout; //重载写法颠倒了,虽然能正常运行,但格式很难看,正常应该是cout<
(1)<<重载函数 不能写在类中,原因:
如果要把 <<函数重载 写在日期类中,日期类默认有this指针,调用void operator<<(std::ostream& out)时因为是双目运算符,第一个参数默认传给this指针,第二个参数传给形参,所以调用时只能写成d1 << cout; 但正常的写法应该是 cout<
(2)<<重载函数 写在类外 要搭配 友元
如果把<<函数重载 写在类外 ,那他将无法访问对象的私有成员变量_year等,所以要在类中加上友元,友元:表明这个外部函数是类的朋友,可以随意访问私有成员变量。但这仍然有问题
(这样仍是存在问题的,未完善)
Date.c:
class Date
{
friend void operator<<(std::ostream& out, const Date& d1);
public:
Date(int year = 1, int month = 1, int day = 1);
private:
int _year;
int _month;
int _day;
};
void operator<<(std::ostream& out,const Date& d1)
{
out << d1._year << "-" << d1._month << "-" << d1._day << endl;
}
(3)<<重载函数 写在类外不能写在头文件中
若写在头文件中,Date.cpp和test.cpp都包含了Date.h,void operator<<(std::ostream& out,const Date& d1) 将会在两个cpp文件展开定义,编译生成两个.o文件,Date.o和test.o的符号表都有这个函数的定义,链接时会冲突,重定义。
那类是在.h文件,类中定义的函数会重定义吗?
答:不会,类中成员函数默认是内联,函数定义不放在头文件中,调用时因为是内联,有函数定义,call指令后面就会有函数的地址,所以就不用链接时去找函数地址,并且有函数定义就根本不用把函数放在符号表里;有函数声明时,call指令后面没有函数地址,只有做过处理的函数名,链接时通过函数名去符号表找函数地址,这样也是可以的。
重定义的解决方案:把<<重载函数定义写在Date.cpp中,Date.h展开就只有函数声明了,call指令后面没有函数地址,只有做过处理的函数名,链接时通过函数名去符号表找函数地址。
(仍需要完善)
Date.c:
class Date
{
friend void operator<<(std::ostream& out, const Date& d1);
public:
Date(int year = 1, int month = 1, int day = 1);
private:
int _year;
int _month;
int _day;
};
Date.cpp:
#include "Date.h"
Date::Date(int year , int month , int day )
{
_year = year;
_month = month;
_day = day;
}
void operator<<(std::ostream& out, const Date& d1)
{
out << d1._year << "-" << d1._month << "-" << d1._day << endl;
}
(4)为支持连续流插入需要设置返回值(完善版本)
完善版本:
Date.c:
class Date
{
friend std::ostream& operator<<(std::ostream& out, const Date& d1);
public:
Date(int year = 1, int month = 1, int day = 1);
private:
int _year;
int _month;
int _day;
};
Date.cpp:
#include "Date.h"
Date::Date(int year , int month , int day )
{
_year = year;
_month = month;
_day = day;
}
std::ostream& operator<<(std::ostream& out, const Date& d1)
{
out << d1._year << "-" << d1._month << "-" << d1._day << endl;
return out;
}
test.cpp:
void TestDate7()
{
Date d1,d2;
cout << d1 << d2;
operator<<(cout, d1);
}
int main()
{
TestDate7();
return 0;
}
3.流提取>> (和流插入<<搭配)
流提取>>类似流插入,只是因为要把值输入到d1中,所以d1不能带const 这一点差别
Date.h:
#include
#include
using std::cout;
using std::cin;
using std::endl;
class Date
{
//流插入<<
friend std::ostream& operator<<(std::ostream& out, const Date& d1);
//流提取>>
friend std::istream& operator>>(std::istream& in, Date& d1);
public:
Date(int year = 1, int month = 1, int day = 1);
private:
int _year;
int _month;
int _day;
};
Date.cpp
#include "Date.h"
Date::Date(int year , int month , int day )
{
_year = year;
_month = month;
_day = day;
}
std::ostream& operator<<(std::ostream& out, const Date& d1)
{
out << d1._year << "-" << d1._month << "-" << d1._day << endl;
return out;
}
std::istream& operator>>(std::istream& in, Date& d1)
{
in >> d1._year >> d1._month >> d1._day;
return in;
}
test.cpp
void TestDate7()
{
Date d1,d2;
cin >> d1 >> d2;
cout << d1 << d2;
}
int main()
{
TestDate7();
return 0;
}
二.初始化列表
引入:
在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称作为类对象成员的初始化,构造函数体中的语句只能将其称作为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
1.定义
初始化列表:就是对象的成员变量定义的地方
2.格式
格式: 成员变量()
成员变量=0;这样直接赋值是错的,会报错
int value = 10;
class Date
{
public:
Date(int year, int n, int a)
:_n(n)
, _ref(value)
, _year(year)
, _aa(a)
{}
private:
int _year;
const int _n;
int& _ref;
A _aa;
};
int main()
{
Date d1(2022, 1, 10); // 对象定义
return 0;
}
3.特征
1.
每个成员变量在初始化列表中
只能出现一次
(
初始化只能初始化一次
)
2.
类中包含以下成员,必须放在初始化列表位置进行初始化:
①引用成员变量
②const成员变量
③自定义类型成员(
该类没有默认构造函数
)
其他的变量既可以在初始化列表初始化,也可以在函数体内初始化
3.
尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
4.
成员变量
在类中
声明次序
就是其在初始化列表中的
初始化顺序
,与其在初始化列表中的先后次序无关
总结:初始化列表可以认为就是对象的成员变量定义的地方,建议尽量在初始化列表初始化,在函数体定义可能会报错或者效率不高等问题。
(1)例子1:都初始化
初始化d1需要把初始化列表走一遍进行初始化
int value = 10;
class A
{
public:
A(int x =0)
:_x(x)
{}
private:
int _x;
};
class Date
{
public:
Date(int year, int n, int a)
:_n(n)
, _ref(value)
, _year(year)
, _aa(a)
{}
private:
// 定义时初始化
// 也可以定义时不初始化,后面再赋值修改
int _year; // 声明
// 只能在定义初始化
const int _n;
int& _ref;
A _aa;
};
int main()
{
Date d1(2022, 1, 10); // 对象定义
return 0;
}
(2)例子2:缺省值和初始化列表
class Stack
{
public:
Stack(int capacity = 0)
{
_a = (int*)malloc(sizeof(int) * capacity);
assert(_a);
_top = 0;
_capacity = capacity;
}
private:
int* _a;
int _top;
int _capacity;
};
class MyQueue
{
public:
MyQueue(int size = 1)
:_size(size)
{}
//拷贝构造也是构造函数,你写了拷贝构造就不会生成默认构造函数,所以MyQueue无可以使用的
//默认构造函数,就会报错
private:
Stack _st1;
Stack _st2;
size_t _size = 0; // 缺省值
};
int main()
{
MyQueue mq;
return 0;
}
初始化时会把初始化列表走一遍,挨个初始化成员变量,在初始化列表中先调用_st1和_st2的默认构造函数把他们初始化,再看初始化列表有_size(size),所以就把_size赋成1,如果初始化列表啥也没有,就把_size赋成缺省值0,
总结就是:初始化列表有值就给值 缺省值就没用了,初始化列表没值就给缺省值。如果缺省值和初始化列表都没给值,那对内置类型不做处理,_size为随机值。(缺省值还是属于在初始化列表初始化的)
(3)对特征4.的解释
4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
看一个选择题:
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();
}
A
.
输出
1
1
B
.
程序崩溃
C
.
编译不通过
D
.
输出
1
随机值
答:选D。初始化列表中的初始化顺序要看成员变量在类中声明次序,与其在初始化列表中的先后次序无关。所以在初始化列表中先初始化_a2,再初始化_a1,选D。
三.explicit关键字
1.用途:
用explicit修饰构造函数,将会禁止单参构造函数的隐式转换。
2.构造函数的隐式类型转换
class Date
{
public:
Date(int year) //构造函数
:_year(year)
{
cout << "Date(int year)" << endl;
}
Date(const Date& d) //拷贝构造函数
{
cout << "Date(const Date& d)" << endl;
}
private:
int _year;
};
int main()
{
int i = 0;
const double& d = i;
Date d1(2022); // 调用构造函数
// 隐士类型的转换
Date d2 = 2022; // 1.构造 + 拷贝构造 -> 优化 合二为一
Date d3(d1); // 2.调用拷贝构造函数
Date d4 = d1; // 3.调用拷贝构造函数
return 0;
}
(1)解释拷贝构造 Date d2 = 2022;
本质是先隐式类型转换:先隐式类型转换本质又是生成一个中间的临时变量,int 类型的2022构造 Date 类型的临时变量,临时变量再拷贝构造给d2。原本是先构造再拷贝构造,通过编译器优化就成了直接构造。
若给构造函数加上explicit,将会禁止构造函数的隐式转换,Date d2 = 2022; 就会报错
(2)解释 const Date& d6=2022 为什么加const
构造函数隐式类型转换,2022先构造一个临时变量,然后让d6成为这个临时变量的引用,但是临时变量具有常性,所以必须加上const才正确。
(3)构造函数的隐式类型转换的语法的意义
让后面学习中的使用更加灵活,比如库中有一个类叫string类:
#include
//库中的string类
//class string
//{
//public:
// string(const char* str)
// {}
//};
void Func(const std::string& s)
{
}
int main()
{
string s2 = "hello";
string s1("hello"); //正常情况是构造一个对象,再把对象传参,但是利用构造函数隐式类型-
Func(s1); //-转换的语法可以写成下面的写法
Func("hello"); //把字符串直接传参就很灵活
return 0;
}
四.static成员
1.概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态的成员变量一定要在类外进行初始化
2.使用的例子:
统计A类型对象创建了多少个?构造和拷贝构造各调用了多少次?
首先我们想到的就是在构造和拷贝构造里面分别设置全局变量 count1 和 count2 计数即可,但是将全局变量写在头文件中时,头文件在各个文件中展开会引发冲突,所以不能用全局变量计数了,我们可以用静态的成员变量计数即,static修饰的成员变量 _count1 和 _count2。(完整调用在第4点静态成员的访问形式)
//int count1 = 0;
//int count2 = 0;
class A
{
public:
A()
{
++_count1;
}
A(const A& aa)
{
++_count2;
}
private:
// 静态成员变量属于整个类,属于类的所有对象
static int _count1;
static int _count2; // 声明
int _a;
};
3.静态成员变量和静态成员函数的特征
1. 静态成员为所有类对象所共享,不属于某个具体的对象(计算类大小时,静态成员不算在内)
2. 静态成员变量必须在
类外 定义
,定义时不添加static关键字(定义要给值,不给值默认是0,最好还是给值)
3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5. 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返值
6.静态变量和全局变量都放在静态区,不是公共代码段
4.静态成员的访问形式
(1)静态成员变量公有时:
①可以通过对象访问
②通过整个类域访问
但如果是私有就不能直接访问
//统计A类型对象创建了多少个?构造和拷贝构造各调用了多少次?
class A
{
public:
A()
{
++_count1;
}
A(const A& aa)
{
++_count2;
}
//private:
// 静态成员变量属于整个类,所以类的所有对象
static int _count1;
static int _count2; // 声明
int _a;
};
// 定义
int A::_count1 = 0;
int A::_count2 = 0;
A Func(A a)
{
A copy(a);
return copy;
}
int main()
{
A a1;
A a2 = Func(a1);
//静态成员变量属于整个类,所以类的所有对象
cout << a1._count1 << endl;
cout << a1._count2 << endl;
cout << a2._count1 << endl;
cout << a2._count2 << endl;
cout << A::_count1 << endl;
cout << A::_count2 << endl;
}
(2)静态成员变量私有时:
为了突破类域,需要在类中设置成员函数去返回私有静态成员变量。
但成员函数调用还需要有对象才能调用,如果没对象呢?——没对象就用静态成员函数
静态成员函数特征:
①静态成员函数没有隐藏的this指针
②不能访问任何非静态成员(因为没有this指针)
class A
{
public:
A()
{
++_count1;
}
A(const A& aa)
{
++_count2;
}
// 成员函数也可以是静态,static成员函数没有this指针
static int GetCount1()
{
//_a = 0; //不能访问非静态成员,因为没有this指针(这个写法错误)
// A aa; //成员函数中再定义对象,用对象访问私有成员变量是可以的(这个写法正确)
// aa._a = 0;
return _count1;
}
static int GetCount2()
{
return _count2;
}
private:
// 静态成员变量属于整个类,所以类的所有对象
static int _count1;
static int _count2; // 声明
int _a;
};
// 定义
int A::_count1 = 0;
int A::_count2 = 0;
A Func(A a)
{
A copy(a);
return copy;
}
int main()
{
A a1;
A a2 = Func(a1);
cout << a1.GetCount1() << endl;
cout << a2.GetCount2() << endl;
cout << A::GetCount1() << endl;
cout << A::GetCount2() << endl;
}
五.C++11 的成员初始化新玩法。
1.C++11支持非静态成员变量在声明时进行初始化赋值
(
这里不是初始化,这里是给声明的成员变
量缺省值
)
有以下三种写法:
class B {
public:
B(int b = 0)
:_b(b)
{}
int _b;
};
class A {
public:
void Print()
{
cout << a << endl;
cout << b._b << endl;
cout << p << endl;
}
private:
// 非静态成员变量,可以在成员声明时给缺省值。
int a = 10; //1.正常给缺省值的写法
B b = 20; //2.内置类型通过 隐式类型转换 给缺省值的写法
int* p = (int*)malloc(4); //3.声明给函数初始化的写法
static int n; //静态成员变量不可以给缺省值,原因在下面
};
int A::n = 10;
int main()
{
A a;
a.Print();
return 0;
}
静态成员变量不可以给缺省值原因:
比如:static int n=10; 这样写就是错的
原因:静态成员变量只有声明在类中,定义在类外,如果你给了缺省值,但是定义没写,静态成员变量也不会在初始化列表初始化;非静态成员变量定义在构造函数的初始化列表中,就算您不写构造函数,也会自动生成默认构造函数,反正 非静态成员变量 肯定会在类中定义,所以他们可以给缺省值。
2.静态成员变量的OJ题:JZ64 求1+2+3+...+n
题目链接:求1+2+3+...+n_牛客题霸_牛客网 (nowcoder.com)
答案:用类Sum构建一个变长数组a[n],数组中有n个对象,构造n个对象需要调用n次jis构造函数,再通过 _i 记录构造调用是第几次,_ret把每一次的 _i 加起来,一直加到n,就相当于了1+2+……+n。
class Sum
{
public:
Sum()
{
_ret+=_i;
++_i;
}
static int GetRet()
{
return _ret;
}
private:
static int _i;
static int _ret;
};
int Sum::_i=1;
int Sum::_ret=0;
class Solution {
public:
int Sum_Solution(int n) {
Sum a[n];
return Sum::GetRet();
}
};
(1)<<重载函数 不能写在类中,原因:
如果要把 <<函数重载 写在日期类中,日期类默认有this指针,调用void operator<<(std::ostream& out)时因为是双目运算符,第一个参数默认传给this指针,第二个参数传给形参,所以调用时只能写成d1 << cout; 但正常的写法应该是 cout< 如果把<<函数重载 写在类外 ,那他将无法访问对象的私有成员变量_year等,所以要在类中加上友元,友元:表明这个外部函数是类的朋友,可以随意访问私有成员变量。但这仍然有问题 若写在头文件中,Date.cpp和test.cpp都包含了Date.h,void operator<<(std::ostream& out,const Date& d1) 将会在两个cpp文件展开定义,编译生成两个.o文件,Date.o和test.o的符号表都有这个函数的定义,链接时会冲突,重定义。 答:不会,类中成员函数默认是内联,函数定义不放在头文件中,调用时因为是内联,有函数定义,call指令后面就会有函数的地址,所以就不用链接时去找函数地址,并且有函数定义就根本不用把函数放在符号表里;有函数声明时,call指令后面没有函数地址,只有做过处理的函数名,链接时通过函数名去符号表找函数地址,这样也是可以的。 重定义的解决方案:把<<重载函数定义写在Date.cpp中,Date.h展开就只有函数声明了,call指令后面没有函数地址,只有做过处理的函数名,链接时通过函数名去符号表找函数地址。 流提取>>类似流插入,只是因为要把值输入到d1中,所以d1不能带const 这一点差别 引入: 虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称作为类对象成员的初始化,构造函数体中的语句只能将其称作为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。 初始化列表:就是对象的成员变量定义的地方 格式: 成员变量() 成员变量=0;这样直接赋值是错的,会报错 其他的变量既可以在初始化列表初始化,也可以在函数体内初始化 初始化d1需要把初始化列表走一遍进行初始化 初始化时会把初始化列表走一遍,挨个初始化成员变量,在初始化列表中先调用_st1和_st2的默认构造函数把他们初始化,再看初始化列表有_size(size),所以就把_size赋成1,如果初始化列表啥也没有,就把_size赋成缺省值0, 总结就是:初始化列表有值就给值 缺省值就没用了,初始化列表没值就给缺省值。如果缺省值和初始化列表都没给值,那对内置类型不做处理,_size为随机值。(缺省值还是属于在初始化列表初始化的) 4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关 看一个选择题: 答:选D。初始化列表中的初始化顺序要看成员变量在类中声明次序,与其在初始化列表中的先后次序无关。所以在初始化列表中先初始化_a2,再初始化_a1,选D。 用explicit修饰构造函数,将会禁止单参构造函数的隐式转换。 本质是先隐式类型转换:先隐式类型转换本质又是生成一个中间的临时变量,int 类型的2022构造 Date 类型的临时变量,临时变量再拷贝构造给d2。原本是先构造再拷贝构造,通过编译器优化就成了直接构造。 若给构造函数加上explicit,将会禁止构造函数的隐式转换,Date d2 = 2022; 就会报错 构造函数隐式类型转换,2022先构造一个临时变量,然后让d6成为这个临时变量的引用,但是临时变量具有常性,所以必须加上const才正确。 声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态的成员变量一定要在类外进行初始化 为了突破类域,需要在类中设置成员函数去返回私有静态成员变量。 但成员函数调用还需要有对象才能调用,如果没对象呢?——没对象就用静态成员函数 ①静态成员函数没有隐藏的this指针 ②不能访问任何非静态成员(因为没有this指针) 比如:static int n=10; 这样写就是错的 原因:静态成员变量只有声明在类中,定义在类外,如果你给了缺省值,但是定义没写,静态成员变量也不会在初始化列表初始化;非静态成员变量定义在构造函数的初始化列表中,就算您不写构造函数,也会自动生成默认构造函数,反正 非静态成员变量 肯定会在类中定义,所以他们可以给缺省值。 题目链接:求1+2+3+...+n_牛客题霸_牛客网 (nowcoder.com) 答案:用类Sum构建一个变长数组a[n],数组中有n个对象,构造n个对象需要调用n次jis构造函数,再通过 _i 记录构造调用是第几次,_ret把每一次的 _i 加起来,一直加到n,就相当于了1+2+……+n。(2)<<重载函数 写在类外 要搭配 友元
(这样仍是存在问题的,未完善)
Date.c:
class Date
{
friend void operator<<(std::ostream& out, const Date& d1);
public:
Date(int year = 1, int month = 1, int day = 1);
private:
int _year;
int _month;
int _day;
};
void operator<<(std::ostream& out,const Date& d1)
{
out << d1._year << "-" << d1._month << "-" << d1._day << endl;
}
(3)<<重载函数 写在类外不能写在头文件中
那类是在.h文件,类中定义的函数会重定义吗?
(仍需要完善)
Date.c:
class Date
{
friend void operator<<(std::ostream& out, const Date& d1);
public:
Date(int year = 1, int month = 1, int day = 1);
private:
int _year;
int _month;
int _day;
};
Date.cpp:
#include "Date.h"
Date::Date(int year , int month , int day )
{
_year = year;
_month = month;
_day = day;
}
void operator<<(std::ostream& out, const Date& d1)
{
out << d1._year << "-" << d1._month << "-" << d1._day << endl;
}
(4)为支持连续流插入需要设置返回值(完善版本)
完善版本:
Date.c:
class Date
{
friend std::ostream& operator<<(std::ostream& out, const Date& d1);
public:
Date(int year = 1, int month = 1, int day = 1);
private:
int _year;
int _month;
int _day;
};
Date.cpp:
#include "Date.h"
Date::Date(int year , int month , int day )
{
_year = year;
_month = month;
_day = day;
}
std::ostream& operator<<(std::ostream& out, const Date& d1)
{
out << d1._year << "-" << d1._month << "-" << d1._day << endl;
return out;
}
test.cpp:
void TestDate7()
{
Date d1,d2;
cout << d1 << d2;
operator<<(cout, d1);
}
int main()
{
TestDate7();
return 0;
}
3.流提取>> (和流插入<<搭配)
Date.h:
#include
二.初始化列表
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
1.定义
2.格式
int value = 10;
class Date
{
public:
Date(int year, int n, int a)
:_n(n)
, _ref(value)
, _year(year)
, _aa(a)
{}
private:
int _year;
const int _n;
int& _ref;
A _aa;
};
int main()
{
Date d1(2022, 1, 10); // 对象定义
return 0;
}
3.特征
1.
每个成员变量在初始化列表中
只能出现一次
(
初始化只能初始化一次
)
2.
类中包含以下成员,必须放在初始化列表位置进行初始化:
①引用成员变量
②const成员变量
③自定义类型成员(
该类没有默认构造函数
)
(1)例子1:都初始化
int value = 10;
class A
{
public:
A(int x =0)
:_x(x)
{}
private:
int _x;
};
class Date
{
public:
Date(int year, int n, int a)
:_n(n)
, _ref(value)
, _year(year)
, _aa(a)
{}
private:
// 定义时初始化
// 也可以定义时不初始化,后面再赋值修改
int _year; // 声明
// 只能在定义初始化
const int _n;
int& _ref;
A _aa;
};
int main()
{
Date d1(2022, 1, 10); // 对象定义
return 0;
}
(2)例子2:缺省值和初始化列表
class Stack
{
public:
Stack(int capacity = 0)
{
_a = (int*)malloc(sizeof(int) * capacity);
assert(_a);
_top = 0;
_capacity = capacity;
}
private:
int* _a;
int _top;
int _capacity;
};
class MyQueue
{
public:
MyQueue(int size = 1)
:_size(size)
{}
//拷贝构造也是构造函数,你写了拷贝构造就不会生成默认构造函数,所以MyQueue无可以使用的
//默认构造函数,就会报错
private:
Stack _st1;
Stack _st2;
size_t _size = 0; // 缺省值
};
int main()
{
MyQueue mq;
return 0;
}
(3)对特征4.的解释
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();
}
A
.
输出
1
1
B
.
程序崩溃
C
.
编译不通过
D
.
输出
1
随机值
三.explicit关键字
1.用途:
2.构造函数的隐式类型转换
class Date
{
public:
Date(int year) //构造函数
:_year(year)
{
cout << "Date(int year)" << endl;
}
Date(const Date& d) //拷贝构造函数
{
cout << "Date(const Date& d)" << endl;
}
private:
int _year;
};
int main()
{
int i = 0;
const double& d = i;
Date d1(2022); // 调用构造函数
// 隐士类型的转换
Date d2 = 2022; // 1.构造 + 拷贝构造 -> 优化 合二为一
Date d3(d1); // 2.调用拷贝构造函数
Date d4 = d1; // 3.调用拷贝构造函数
return 0;
}
(1)解释拷贝构造 Date d2 = 2022;
(2)解释 const Date& d6=2022 为什么加const
(3)构造函数的隐式类型转换的语法的意义
让后面学习中的使用更加灵活,比如库中有一个类叫string类:
#include
四.static成员
1.概念
2.使用的例子:
统计A类型对象创建了多少个?构造和拷贝构造各调用了多少次?
首先我们想到的就是在构造和拷贝构造里面分别设置全局变量 count1 和 count2 计数即可,但是将全局变量写在头文件中时,头文件在各个文件中展开会引发冲突,所以不能用全局变量计数了,我们可以用静态的成员变量计数即,static修饰的成员变量 _count1 和 _count2。(完整调用在第4点静态成员的访问形式)
//int count1 = 0;
//int count2 = 0;
class A
{
public:
A()
{
++_count1;
}
A(const A& aa)
{
++_count2;
}
private:
// 静态成员变量属于整个类,属于类的所有对象
static int _count1;
static int _count2; // 声明
int _a;
};
3.静态成员变量和静态成员函数的特征
1. 静态成员为所有类对象所共享,不属于某个具体的对象(计算类大小时,静态成员不算在内)
2. 静态成员变量必须在
类外 定义
,定义时不添加static关键字(定义要给值,不给值默认是0,最好还是给值)
3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5. 静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返值
6.静态变量和全局变量都放在静态区,不是公共代码段
4.静态成员的访问形式
(1)静态成员变量公有时:
①可以通过对象访问
②通过整个类域访问
但如果是私有就不能直接访问
//统计A类型对象创建了多少个?构造和拷贝构造各调用了多少次?
class A
{
public:
A()
{
++_count1;
}
A(const A& aa)
{
++_count2;
}
//private:
// 静态成员变量属于整个类,所以类的所有对象
static int _count1;
static int _count2; // 声明
int _a;
};
// 定义
int A::_count1 = 0;
int A::_count2 = 0;
A Func(A a)
{
A copy(a);
return copy;
}
int main()
{
A a1;
A a2 = Func(a1);
//静态成员变量属于整个类,所以类的所有对象
cout << a1._count1 << endl;
cout << a1._count2 << endl;
cout << a2._count1 << endl;
cout << a2._count2 << endl;
cout << A::_count1 << endl;
cout << A::_count2 << endl;
}
(2)静态成员变量私有时:
静态成员函数特征:
class A
{
public:
A()
{
++_count1;
}
A(const A& aa)
{
++_count2;
}
// 成员函数也可以是静态,static成员函数没有this指针
static int GetCount1()
{
//_a = 0; //不能访问非静态成员,因为没有this指针(这个写法错误)
// A aa; //成员函数中再定义对象,用对象访问私有成员变量是可以的(这个写法正确)
// aa._a = 0;
return _count1;
}
static int GetCount2()
{
return _count2;
}
private:
// 静态成员变量属于整个类,所以类的所有对象
static int _count1;
static int _count2; // 声明
int _a;
};
// 定义
int A::_count1 = 0;
int A::_count2 = 0;
A Func(A a)
{
A copy(a);
return copy;
}
int main()
{
A a1;
A a2 = Func(a1);
cout << a1.GetCount1() << endl;
cout << a2.GetCount2() << endl;
cout << A::GetCount1() << endl;
cout << A::GetCount2() << endl;
}
五.C++11 的成员初始化新玩法。
1.C++11支持非静态成员变量在声明时进行初始化赋值
(
这里不是初始化,这里是给声明的成员变
量缺省值
)
有以下三种写法:
class B {
public:
B(int b = 0)
:_b(b)
{}
int _b;
};
class A {
public:
void Print()
{
cout << a << endl;
cout << b._b << endl;
cout << p << endl;
}
private:
// 非静态成员变量,可以在成员声明时给缺省值。
int a = 10; //1.正常给缺省值的写法
B b = 20; //2.内置类型通过 隐式类型转换 给缺省值的写法
int* p = (int*)malloc(4); //3.声明给函数初始化的写法
static int n; //静态成员变量不可以给缺省值,原因在下面
};
int A::n = 10;
int main()
{
A a;
a.Print();
return 0;
}
静态成员变量不可以给缺省值原因:
2.静态成员变量的OJ题:JZ64 求1+2+3+...+n
class Sum
{
public:
Sum()
{
_ret+=_i;
++_i;
}
static int GetRet()
{
return _ret;
}
private:
static int _i;
static int _ret;
};
int Sum::_i=1;
int Sum::_ret=0;
class Solution {
public:
int Sum_Solution(int n) {
Sum a[n];
return Sum::GetRet();
}
};



