每天反复做的事情造就了我们,然后你就会发现,优秀并非一种行为,而是一种习惯。
------------亚里士多德(Aristotle(阿瑞斯透头))
银行里柜台里坐着的数钱的----(点钞员)
在计算机世界里我们将具备这些特质的人 抽象为一个类
我们用类创建的每个对象将拥有这些特质
class BankPerson
{
public:
CheckMoney()
{
//数钱
}
};
void main()
{
BankPerson B1;//创建对象 数钱人1
BankPerson B2;//创建对象 数钱人2
//他们都具备一个功能数钱
}
C++中struct和class的区别是什么?
C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来 定义类。
和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类 默认访问权限是private。
注意:在继承和模板参数列表位置,struct和class也有区别,后序给大家介绍。
类的定义
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分
号不能省略。
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者
成员函数。
类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 ::
作用域操作符指明成员属于哪个类域。
class Person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int _age;
};
// 这里需要指定PrintPersonInfo是属于Person这个类域
void Person::PrintPersonInfo()
{
cout << _name << " "<< _gender << " " << _age << endl;
}
类的实例化
用类类型创建对象的过程,称为类的实例化
1. 类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;比如:入学时填写的学生信息表,表格就可以看成是一个 类,来描述具体学生信息。
2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
int main()
{
Person._age = 100; // 编译失败:error C2059: 语法错误:“.”
return 0;
}
Person类是没有空间的,只有Person类实例化出的对象才有具体的年龄。
做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设
计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象
才能实际存储数据,占用物理空间
this指针
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1, d2;
d1.Init(2022,1,11);
d2.Init(2022, 1, 12);
d1.Print();
d2.Print();
return 0;
}
对于上述类,有这样的一个问题:
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函 数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏 的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量” 的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编 译器自动完成。
this指针特性
- this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。(Date* const this)
- 只能在“成员函数”的内部使用
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储this指针。
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递
1. this指针存在哪里?
其实编译器在生成程序时加入了获取对象首地址的相关代码。并把获取的首地址存放在了
寄存器ECX中(VC++编译器是放在ECX中,其它编译器有可能不同)。也就是成员函数的其它
参数正常都是存放在栈中。而this指针参数则是存放在寄存器中。
类的静态成员函数因为没有this指针这个参数,所以类的静态成员函数也就无法调用类的非静态成员变量。
-------------------------------------------------------------------------------------------
2. this指针可以为空吗?
可以为空,当我们调用函数时,如果函数内部不需要使用到this,也就是不需要通过this指向当前对象
并对其进行操作时才可以为空(当我们在其中什么都不做或者在里面随便打印一个字符串),如果调用的
函数需要指向当前对象,并进行操作,则会发生错误(空指针引用)就跟C中一样不能进行空指针的引用
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
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;
};
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
explicit关键字
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用。
用explicit修饰构造函数,将会禁止构造函数的隐式转换。
禁止隐式转换后就不能编译通过了
static成员
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
class A
{
public:
A() { ++_scount; }
A(const A& t) { ++_scount; }
~A() { --_scount; }
static int GetACount() { return _scount; }
private:
static int _scount;
};
int A::_scount = 0;
void TestA()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
}
特性
- 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
- 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
- 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员也是类的成员,受public、protected、private 访问限定符的限制
静态成员函数可以调用非静态成员函数吗?
可以通过传递this指针的方法
静态成员函数是类的成员,但非类的对象的成员,//因为没有this指针,若我们能传递this指针依然可以对其进行调用
#include <string>
#include <iostream>
using namespace std;
class A
{
public:
static void test(void* arg)
{
A* p = (A*)arg;
cout << p << endl;
cout << p->m_data<<endl;
}
void test2()
{
test(this);
cout << "this pointer" << endl;
cout << this << endl;
}
private:
int m_data = 888;
};
int main()
{
A a;
a.test2();
return 0;
}
在类中使用静态成员函数是一种破坏封装的行为,因为静态成员函数只能调用类的静态成员。
但是在有些情况下只能使用静态成员函数,比如类内绑定自身成员函数作为回调函数,这种
情况在开启多线程时很常见,如果不想将回调定义为全局那只能定义为类静态了,为了避免
过度破坏封装类中应当尽量不要让类静态成原函数调用类成员。这种情况下可以用一种比较
取巧的方法。
因为类的静态成员和普通成员其实就一种区别,那就是静态成员本身没有this指针,所以
静态成员属于类而不属于类对象。如果我们想在类的静态成员函数里面调用类的普通成员,
只需要把类指针当做参数传入静态成员函数里面,静态成员函数可以使用这个指针调用类的
普通成员,
非静态成员函数可以调用类的静态成员函数吗?
非static成员函数可访问static态成员函数/成员;
#include <string>
#include <iostream>
using namespace std;
class A
{
public:
void test2()
{
cout << y_data<<endl;
}
private:
static int y_data;
};
int A::y_data = 999;
int main()
{
A a;
a.test2();
return 0;
}
友元
http://t.csdn.cn/PHnNF
去看这个博主,他 他妈写的太好了!!!!
生活中你的家有客厅(public),有你的卧室(private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去
但是呢,你也可以允许 隔壁老王 进去。
在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的目的 就是让一个函数或者类 访问另一个类中的私有成员
友元的关键字为 friend
全局函数做友元
#include <iostream>
#include <string>
using namespace std;
// 房屋类
class Building
{
// 告诉编译器 laoWang全局函数是 Building类 的好朋友,可以访问Building对象的私有成员
friend void laoWang1(Building *building);
friend void laoWang2(Building &building);
friend void laoWang3(Building building);
public:
Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
string m_SittingRoom; // 客厅
private:
string m_BedRoom; // 卧室
};
//全局函数
void laoWang1(Building *building)
{
cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_SittingRoom << endl;
cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_BedRoom << endl;
}
void laoWang2(Building &building)
{
cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_SittingRoom << endl;
cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_BedRoom << endl;
}
void laoWang3(Building building)
{
cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_SittingRoom << endl;
cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_BedRoom << endl;
}
void test()
{
Building building;
laoWang1(&building);
laoWang2(building);
laoWang3(building);
}
int main()
{
test();
}
类做友元
#include <iostream>
#include <string>
using namespace std;
// 类作友元
class Building;
class LaoWang
{
public:
LaoWang();
void visit(); //参观函数 访问Building中的属性
Building * building;
private:
};
// 房屋类
class Building
{
// 告诉编译器,LaoWang类是Building类的好朋友,可以访问Building类的私有成员
friend class LaoWang;
public:
Building();
string m_SittingRoom; // 客厅
private:
string m_BedRoom; // 卧室
};
// 类外定义成员函数
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
LaoWang::LaoWang()
{
// 创建建筑物对象
building = new Building;
}
void LaoWang::visit()
{
cout << "隔壁老王LaoWang类正在访问:" << building->m_SittingRoom << endl;
cout << "隔壁老王LaoWang类正在访问:" << building->m_BedRoom << endl;
}
void test()
{
LaoWang lw;
lw.visit();
}
int main()
{
test();
return 0;
}
成员函数做友元
被声明为friend的类的函数可以访问该类的私有成员
visit1()//被声明为Building的朋友 ,可以访问Building的私有成员
visit2()//并非Building的朋友
#include <iostream>
#include <string>
using namespace std;
class Building;
class LaoWang
{
public:
LaoWang();
void visit1(); //让visit1()函数 可以 访问Building中的私有成员
void visit2(); //让visit2()函数 不可以 访问Building中的私有成员
Building *building;
private:
};
class Building
{
// 告诉编译器,LaoWang类下的visit1()函数是Building类的好朋友,可以访问Building的私有成员
friend void LaoWang::visit1();
public:
Building();
string m_SittingRoom; //客厅
private:
string m_BedRoom; //卧室
};
LaoWang::LaoWang()
{
building = new Building;
}
void LaoWang::visit1()
{
cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_SittingRoom << endl;
cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_BedRoom << endl;
}
void LaoWang::visit2()
{
cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_SittingRoom << endl;
//cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_BedRoom << endl; //错误!私有属性不可访问
}
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
void test()
{
LaoWang lw;
lw.visit1();
lw.visit2();
}
int main()
{
test();
return 0;
}
特征
-
友元函数可访问类的私有和保护成员,但不是类的成员函数
-
友元函数不能用const修饰
-
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
-
一个函数可以是多个类的友元函数
-
友元函数的调用与普通函数的调用原理相同
-
A是B的友元类(A----B的private) 但是我A可没说你B是我A的朋友(B !--->A)
内部类
特征
-
内部类可以定义在外部类的public、protected、private都是可以的。
-
注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
-
sizeof(外部类)=外部类,和内部类没有任何关系。
class A
{
private:
static int k;
int h;
public:
class B // B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
int A::k = 1;
int main()
{
A::B b;
b.foo(A());
return 0;
}
匿名对象
//匿名对象产生的三种场景
#include<iostream>
using namespace std;
class Point{
public:
Point(int a,int b){
cout << "有参构造函数被调用了1" << endl;
this->x = a;
this->y = b;
}
Point(Point &a1){
cout << "拷贝构造函数被调用了2" << endl;
this->x = a1.x;
this->y = a1.y;
}
~Point(){
cout << "析构函数被调用了3" << endl;
cout << "x=" << x << endl;
cout << "y=" << y << endl;
}
Point Protset(int a){
this->x = a;
return *this;
//执行 return *this; 会产生一个匿名对象,作为返回值
//强调:如果返回值是引用,则不会产生匿名对象
}
Point Protset2(int a){
Point temp(a, a);
return temp;
//执行 return temp;会先产生一个匿名对象,执行拷贝构造函数,作为返回值,
//然后释放临时对象temp
}
//总结:函数返回值为一个对象(非引用)的时候会产生一个匿名对象,匿名对象根据主函数的操作决定生命周期
Point& Protset3(int a){
Point temp(a, a);
return temp;
//执行 return temp;不会产生匿名对象,而是会将temp的地址先赋值到引用中,
//在释放temp的内存,此时Point&得到是一个脏数据
}
void PrintfA()const{
cout << "x="<<x << endl;
cout << "y=" << y << endl;
}
private:
int x;
int y;
};
void ProtectA(){
//生成一个匿名对象,因为用来初始化另一个同类型的对象,这个匿名对象会直接转换成新的对象,
//减少资源消耗
Point p1 = Point(1,1);
/*Point p2(2, 2);
p2 = p1.Protset(3);
p2.PrintfA();*/
//观察发现p2打印出正确数据,因此得出结论p1.Protset(3);返回来一个匿名对象,
//但是这个匿名对象执行完"="之后,才会被释放
Point p4(5, 5);
p4=p1.Protset2(4);
p4.PrintfA();
}
void main(){
ProtectA();
system("pause");
}
class Solution {
public:
int Sum_Solution(int n) {
//通过与运算判断n是否为正数,以结束递归
n && (n += Sum_Solution(n - 1));
return n;
}
};
class Solution {
public:
class sum {
public:
sum() {
m_sum += m_i;
m_i++;
}
};
int Sum_Solution(int n) {
m_i=1;
sum obj[n];//调用拷贝构造函数n次
return m_sum;
}
static int m_i;
static int m_sum;
};
int Solution::m_i = 0;
int Solution::m_sum = 0;