目录
- 1.再谈析构函数
- 1.1构造函数体赋值
- 1.2 初始化列表
- 1.3 explicit关键字
- 2.Static成员
- 2.1概念
- 2.2 特性
- 3.友元
- 3.1 概念
- 3.2友元函数
- 3.3 友元类
- 4.内部类
- 4.1 概念
- 5.匿名对象
- 5.1 概念
- 6.拷贝对象时的一些编译器优化
- 7.再次理解封装
1.再谈析构函数
1.1构造函数体赋值
在对类的实例化后,也就是创建对象,都会自动调用构造函数
当对象中某一个成员已经有了初始值,但其他成员变量仍需要初始化的时候,构造函数体中的语句只能将其称为赋初值,初始化只能初始化一次,而构造函数体内可以多此赋值
1.2 初始化列表
初始化列表的概念:是一种用于在对象创建时初始化成员变量的语法。
初始化列表的组成:
以一个冒号(:)开始,接着是一个以逗号(,)分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。这些初始值将按照它们在初始化列表中的顺序来初始化对象的成员变量。
class M
{
public:
M(char name, int age, char sex):_name(name),_age(age),_sex(sex)
{
if (_age < 0)
cout << "输入错误" << endl;
else
{
printf("%c", "%c", _name, _sex);
}
}
private:
char _name;
int _age;
char _sex;
};
注意:
- 每个成员变量在初始化列表中只能出现一次
- 引用成员,const变量,自定义类型成员(无默认构造函数) ,必须放在初始化列表位置初始化
class A
{
public:
A(int num) :_num(num)
{
}
private:
int _num;
};
class M
{
public:
M(int x,char name, char sex):_aa(x),_name(name),_sex(sex)
{
//if (_age < 0)
//cout << "输入错误" << endl;
}
private:
char& _name;//引用
const char _sex;//常变量
A _aa;//自定义
};
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
class N
{
public:
N(int a=2) : _b(a),_a(_b) //在这的顺序是先赋值给_b,再赋值给_a
{
cout << _a << endl << _b << endl;
}
private:
int _a;
int _b;
};
int main()
{
N n;
return 0;
}
1.3 explicit关键字
explicit
关键字用于修饰单参数构造函数,它表明该构造函数是显式的,即不会隐式地被用于隐式类型转换。
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用。
不加explicit:
class N
{
public:
//这里不加explicit
//explicit N*(int a =2)
N(int a=2)
: _a(a)
{
cout << _a << endl;
}
private:
int _a;
};
void fun(N n)
{
}
int main()
{
fun(1);//本应该传自定义类型N,但是编译器做了隐式转换,直接转换成 n(1)
return 0;
}
加了explicit:
编译器会直接报错,不能实现隐式转换
2.Static成员
2.1概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用
static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
静态成员是属于类本身而不是类的实例的成员。静态成员可以被所有该类的实例共享,并且可以通过类名访问,而不需要创建类的实例。这使得静态成员成为存储类数据和提供类范围功能的有用工具。
class A
{
public:
A() {
_a++;
}
static int num()
{
return _a;
}
private:
static int _a;
};
int A::_a = 0;
int main()
{
A a1, a2;
cout << A::num() << endl;
return 0;
}
2.2 特性
- 静态成员为所有类对象所共享,不属于某一个具体的对象,储存在静态区(也就与对象实例化无关)
- 静态成员变量必须在类外定义,定义时不添加static关键字
- 静态成员函数没有隐藏的**
this
**指针,不能访问非静态成员 - 静态成员受public,private,protected访问限定符得限制
注意:静态成员函数是声明为静态的类函数。它们不操作特定的对象实例,因此不能访问非静态成员变量或非静态成员函数。可以通过类名直接调用静态成员函数。
3.友元
3.1 概念
友元(friend)是一种机制,允许一个类或函数访问另一个类的私有成员。通过将一个函数或类声明为另一个类的友元,可以允许该函数或类访问该类的私有成员,即使它们通常不能直接访问这些私有成员。
友元提供的是一种突破封装的方式(类比现实中的"开后门")
友元分为:友元函数和友元类
3.2友元函数
class Date
{
public:
friend void Print(const Date a);
Date(int year=2024, int month=2, int day=29) :_year(year), _month(month), _day(day){}
private:
int _year;
int _month;
int _day;
};
void Print(const Date a)
{
cout << a._day << endl;
}
关于类的输出输入流(友元应用)
class Date
{
public:
friend ostream& operator<<(ostream& cout, const Date& a);
friend istream& operator>>(istream& cin, Date& a);
//friend void Print(const Date& a);
Date(int year=2024, int month=2, int day=29) :_year(year), _month(month), _day(day){}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& cout, const Date& a)
{
cout << a._day << " " << a._month << " " << a._year << endl;
return cout;
}
istream& operator>>(istream& cin, Date& a)
{
cin >> a._year >> a._month >> a._day;
return cin;
}
int main()
{
Date a;
cin >> a;
cout << a;
return 0;
}
友元函数不是类的成员函数
友元函数不能用const修饰
3.3 友元类
友元类的所有成员函数都可以是另一个友元函数,访问另一个类中的非公有成员
- 友元关系单向,不具有交换性(你是我朋友,不代表我是你朋友)
- 友元关系不能传递
- 不能继承
class Date
{
public:
friend class A;
friend ostream& operator<<(ostream& cout, const Date& a);
friend istream& operator>>(istream& cin, Date& a);
//friend void Print(const Date& a);
Date(int year=2024, int month=2, int day=29) :_year(year), _month(month), _day(day){}
private:
int _year;
int _month;
int _day;
};
class A
{
public:
void Print()
{
//访问Date的私有成员
cout << _num << endl;
cout << t._day << " " << t._month << " " << t._year << endl;
cout << t;
}
private:
int _num = 10;
Date t;
};
int main()
{
A a;
a.Print();
return 0;
}
4.内部类
4.1 概念
如果一个类定义在另一个类的内部,这个内部类就叫做内部类
它不属于外部,不能通过外部的类的对象去访问内部类的成员
内部类是外部类的友元类,内部类可以访问外部类的对象参数,但是外部类不是内部类的友元
class A
{
private:
int _x = 10;
static int a;
public:
class B
{
private:
int _y = 5;
public:
void Print(const A& _A)
{
cout << a << endl;
cout << _A._x << endl;
}
};
};
int A::a = 0;
int main()
{
A::B b;
b.Print(A());
return 0;
}
5.匿名对象
5.1 概念
匿名对象是指在不给对象命名的情况下创建的临时对象。这些对象通常用于某些特定的场景,而不需要通过变量名来引用它们。
class A
{
private:
int _x ;
public:
A(int x=10) :_x(x)
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
void Print()
{
cout << _x;
}
};
int main()
{
A();//生命周期只有这一行
A().Print();//只调用函数,那么匿名对象非常快捷
return 0;
}
6.拷贝对象时的一些编译器优化
在传参嗯哼传返回值的过程中,一般编译器会做一些优化来提高效率(减少不必要的对象的拷贝)
class A
{
private:
int _x;
public:
A(int x = 10) :
_x(x)
{
cout << "A(x)" << endl;
}
A(const A& a)
{
_x = a._x;
cout << "A(const A& a)" << endl;
}
A& operator=(const A& a)
{
if(this!=&a)
{
_x = a._x;
cout << "operator=" << endl;
}
return *this;
}
~A()
{
cout << "~A" << endl;
}
};
void fun(A a)
{}
A fun2()
{
A a;
return a;
}
传值传参
int main()
{
A a(2);
fun(a);
return 0;
}
int main()
{
fun(A(1));//连续构造+拷贝-》优化为一个构造
return 0;
}
int main()
{
// fun(A(1));//连续构造+拷贝-》优化为一个构造
A a = fun2();// 原:函数体一个构造,返回值一个构造,拷贝一个构造-》优化为一个构造
return 0;
}
7.再次理解封装
现实生活中的实体计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识。
类是对某一类实体(对象)的来进行描述的,描述该对象的特征属性