const修饰成员函数
有const修饰的函数不能调用没有const修饰的函数;没有const修饰的函数可以调用有const修饰的函数。
java
#include<iostream>
using namespace std;
//创建类
class Date {
public:
//构造函数
Date(int year = 0, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
//拷贝构造
Date(const Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
}
////打印
//void Print() {
// cout << _year << "-" << _month << "-" << _day << endl;
//}
// //编译器编译为
/*void Print(Date* this ) {
cout << this->_year << "-" <<this-> _month << "-" << this->_day << endl;
}*/
//打印,在函数后面加上const修饰
void Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
//编译器编译为
/*void Print(const Date* this ) {
cout << this->_year << "-" <<this-> _month << "-" << this->_day << endl;
}*/
private:
int _year;
int _month;
int _day;
};
//void fun(Date& d) {
// d.Print();
//}
//若在参数内加const修饰,d就不能调用Print函数,
// 因为它传入的参数是非const修饰的,我们要在Print函数后加上const修饰后才能继续调用
void fun(const Date& d) {
d.Print();//编译器编译为d.Print(&d);
}
int main() {
Date d1(2026, 4, 22);
fun(d1);
return 0;
}

注意:
java
const Date* p1 ->*p1指向对象
Date const * p2 -> *p2指向对象
Date* const p3 ->p3指向指针本身
类中成员变量的初始化
类中的构造函数体中的语句不能称为初始化,因为初始化只能初始化一次,而构造函数体内可以赋值多次,所以只能称其为赋初值。
怎样对类中成员变量的初始化?
以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。如下代码:
java
class Time {
public:
//初始化
Time(int hour, int second, int minute)
: _hours(hour)//初始化,每个成员变量只在初始化列表中初始化一次
, _seconds(second)
, _minutes(minute)
{
}
private:
int _hours;
int _seconds;
int _minutes;
};
注意:必须在初始化列表初始化的成员变量
1.引用成员变量
2.const成员变量
3.自定义类型成员变量
java
class A {
public:
A(int a)
:_a(a)
{}
private:
int _a;
};
class B {
public:
//初始化成员变量
B(int a, int f1)
:_a1(a)
, f(f1)//对引用初始化,等价于=》int& f = f1;
, n(10)
{}
private:
A _a1;//声明,没有默认构造函数
int& f;//引用,只是声明,不是定义
const int n;//const修饰
};
警告小贴士:
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

如图:先初始化_b1,因为此时_b2是随机值,所以_b1被初始化为随机值了,_b2被初始化为10。
explicit关键字的使用
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。使用explicit关键字可以禁止类型转换。
java
//explicit关键字的使用
class Date
{
public:
//无explicit修饰具有类型转换的作用
// explicit修饰构造函数,禁止类型转换
Date(int year = 0, int month = 1, int day = 1)//去掉explicit修饰之后,代码可以通过编译
:_year(year)
,_month(month)
,_day(day)
{
cout << "Date(int year = 0, int month = 1, int day = 1)" << endl;
}
/*
// 多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具有类型转换作用
// explicit修饰构造函数,禁止类型转换
explicit Date(int year, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
*/
//拷贝构造
//拷贝构造
Date(const Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
cout << "Date(const Date& d)" << endl;
}
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
cout << "Date& operator=(const Date& d)" << endl;
return *this;
}
void Print() {
cout << _year << endl;
}
private:
int _year;
int _month;
int _day;
};
void Test()
{
Date d1(2022);
d1 = 2023;//隐式类型转换,加上explicit后就编译错误。先用2023构造出零时对象tmp,
//【再用 tmp 赋值给 d1(调用 d1.operator=(tmp)),本例中会自动生成赋值=的默认函数,不用写也会自动生成】,其他类型的会进行拷贝构造
d1.Print();
}
int main() {
Test();
return 0;
}

C++11支持的写法
构造函数传参
java
#include<iostream>
using namespace std;
class Date {
public:
//构造函数
Date(int year = 0, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day) {
cout << "Date(int year = 0, int month = 1, int day = 1)" << endl;
cout << _year << "-" << _month << "-" << _day << endl;
}
//拷贝构造
Date(const Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
cout << "Date(const Date& d)" << endl;
}
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
cout << "Date& operator=(const Date& d)" << endl;
return *this;
}
private:
int _year;
int _month;
int _day;
};
int main() {
Date d1(2026, 4, 23);
d1 = { 1, 2, 3 };//先调用构造函数构造出tmp(1,2,3)临时变量,再调用赋值运算符重载把tmp赋值给d1.因为:
//d1 已经是一个完整的 Date 对象了,C++ 不允许你直接把{ 1,2,3 } 塞给 d1
//编译器只能:先用{ 1,2,3 } 造出一个临时的 Date 对象tmp,再把这个临时对象赋值给 d1。
Date d2 = { 4,5,6 };//c++11才支持,只调用了构造函数,为什么?不是把某种类型转成 Date,而是直接用参数列表构造 Date,所以不是隐式类型转换!
Date d3 = 1;//传入一个参数,表示年
Date d4 = { 1,2 };//传入两个参数时,表示年和月
return 0;
}

在成员变量声明时赋初始值
java
//#include<iostream>
//using namespace std;
//class Date {
//public:
// void Print() {
// cout << _year << "-" << _month << "-" << _day << endl;
// }
//private:
// int _year;
// int _month;
// int _day;
//};
//int main() {
// Date d1;
// d1.Print();//输出随机值
// return 0;
//}
#include<iostream>
using namespace std;
class Date {
public:
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
//C++11
int _year = 0;
int _month = 1;
int _day = 1;
};
int main() {
Date d1;
d1.Print();//0-1-1
return 0;
}

static关键字
特点:
-
静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
-
静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
-
类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
-
静态成员函数没有隐藏的this指针(this指针:存储对象的地址),不能访问任何非静态成员
-
静态成员也是类的成员,受public、protected、private 访问限定符的限制
java
#include<iostream>
using namespace std;
class A {
public:
A() {
++n;
}
A(const A& a) {
++n;
}
static int GetN() {
//cout << n << endl;编译错误,为什么静态函数没有this指针?
//因为static修饰的函数叫静态成员函数,函数属于类,不属于任何对象调用,所以不需要this指针,
// 注意:所有函数都在代码段(静态,普通函数)
return n;
}
private:
static int n;//声明
};
//定义
int A::n = 0;
A& f1(A& a) {
return a;
}
int main() {
A a1;
A a2;
f1(a1);
f1(a2);
cout << A::GetN() << endl;
return 0;
}

友元
友元是一种突破封装的方式,因此会增加耦合度,破坏了封装。但有时仍然可以提供便利。
普通函数调用类里面的私有成员,以如下方式类外函数不能调用类中的私有限制的成员变量
java
#include<iostream>
using namespace std;
class Date {
public:
Date(int year = 0, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year = 0;
int _month = 1;
int _day = 1;
};
void f(Date& d) {
//d._year = 10;编译报错,不能调用类里面的成员变量
//cout << d._year << endl;//编译报错
}
int main() {
Date d1;
d1.Print();
f(d1);
return 0;
}
友元函数
特征
友元函数可访问类的私有和保护成员
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用原理相同
在类中声明类外的函数为友元函数,函数就可以调用类里面的私有成员
java
//在类中声明类外的函数为友元函数,函数就可以调用类里面的私有成员
#include<iostream>
using namespace std;
class Date {
friend void f(Date& d);//声明了f(Date& d)为友元函数
public:
Date(int year = 0, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year = 0;
int _month = 1;
int _day = 1;
};
void f(Date& d) {
d._year = 10;//可以调用类里面的成员变量
cout << d._year << endl;
}
int main() {
Date d1;
d1.Print();
f(d1);
return 0;
}

友元函数的使用,重载cout和cin函数
在类中重载
在类中重载operator<<,d1 << cout; 等价于=》 d1.operator<<(&d1, cout); 不符合常规调用,因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧区调用类中的函数,可读性不高,所以我们可以写在类外让该函数成为类中的友元函数
java
#include<iostream>
using namespace std;
//重载operator<<函数
class Date {
public :
//构造函数
Date(int year = 0, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
//在类中重载operator<<
// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用
// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧区调用类中的函数,
// 可读性不高,所以我们可以写在类外让该函数成为类中的友元函数
ostream& operator<<(ostream& out) {
out << _year << "-" << _month << "-" << _day << endl;
return out;
}
private:
int _year;
int _month;
int _day;
};
int main() {
Date d1(2026, 4, 24);
d1 << cout;
d1.operator<<(cout);
return 0;
}

在类外重载,使其变为友元函数,可以调用类中的私有成员变量
1.重载cout函数
java
//在类外重载,使其变为友元函数,可以调用类中的私有成员变量
#include<iostream>
using namespace std;
//重载operator<<函数
class Date {
friend ostream& operator<<(ostream& out, const Date& d);
public :
//构造函数
Date(int year = 0, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, const Date& d) {
out << d._year << "-" << d._month << "-" << d._day << endl;
return out;
}
int main() {
Date d1(2026, 4, 24);
cout << d1;
return 0;
}

2.重载cin函数
java
//在类外重载,使其变为友元函数,可以调用类中的私有成员变量
#include<iostream>
using namespace std;
//重载operator>>函数
class Date {
friend istream& operator>>(istream& in, Date& d);
public :
//构造函数
Date(int year = 0, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
istream& operator>>(istream& in, Date& d) {
in >> d._year;
in >> d._month;
in >> d._day;
return in;
}
int main() {
Date d1;
cin >> d1;
d1.Print();
return 0;
}

友元类
友元类表示一个类是另一个类的友元类,
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
特点:
友元关系是单向的,不具有交换性。比如下面A类和B类,在A类中声明B类为其友元类,那么可以在B类中直接访问A类的私有成员变量,但想在A类中访问B类中私有的成员变量则不行。
友元关系不能传递:如果C是B的友元, B是A的友元,则不能说明C时A的友元。