目录
一、面向过程和面向对象的初步认识
二、引入类
三、类的定义
四、类的访问限定符和封装
1.访问限定符
2.封装
五、类的作用域
六、类的实例化
七、类的大小
八、this指针
C语言是面向过程的,分析出求解问题的方法,调用函数解决问题。
C++是面向对象的,将一件事分出不同的对象,依靠对象间的交互完成。
C语言结构体不支持成员函数,但C++结构体支持。C++的class与struct本质没有区别,唯一区别在于默认时class的成员访问属性为私有,struct为公有。
C++定义结构体类型的变量时,类型名可以不加struct。
struct的升级版——class
class className
{
//类体:成员函数(方法)和成员变量(属性)
};
class是定义类的关键字。className是类名,{}里是类的主体。注意}最后的分号不能省略。
类中的内容称为类的成员:变量称为类的属性或成员变量;类中的函数称为类的方法或者成员函数。
c++的类可以函数在上,变量在下。或者变量在上,函数在下。c++把类看作整体,搜索的时候在整个类里找。顺序不重要,不影响效率。
c语言习惯先定义或者声明再使用。c语言每个变量和函数之间没有必定的关联,是独立的。
class算不算一个域?
算。
那能不能和命名空间一样用 " :: "域作用限定符直接去访问类里面的成员?
比如:Person ::_age=1;//错的
1.成员变量不能。因为class和struct一样,只是定义了一个类型,还没有创建实体,没有开辟空间。所以不能访问。就算把成员变量改成公有的,也不能通过类直接访问。类只是声明,还没有实体。
2.成员函数不能直接通过类访问。成员函数的调用需要传递this指针。后面说。
1.声明和定义一起写:但是直接在类里面定义成员函数,编译器可能会把它当做内联函数。
2.类的声明在.h文件中写,成员函数的定义分离出来在.cpp写。
声明和定义分离的意义:方便阅读。看看到底有哪些方法。
例:
例:日期类:不用_的话,有点难分清谁是谁
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
public(公有) protected(保护) private(私有)
struct和class的区别
面向对象的三大特性:封装、继承、多态。
只能通过规范的方式访问数据就是封装。
c++把数据和方法都放在了类里,而c语言是分开的。在类里用访问限定符修饰成员,想让类外使用就公有,不想被类外访问就私有。封起来是为了更好的管理。
c++要访问数据只能调用函数,要增删查改数据只能调用公有的函数。
例:st.Top();
c语言规范一点应该也是调用函数访问数据,但也能不规范操作。
例:
规范:StackTop(&st);
不规范:st.a[st.top-1]; // 这里要看top初始化是0还是-1。0就是插入,++,top就是最后一个数据的下一个位置。如果top初始化是-1,那top就是最后一个数据的下标。所以说,容易弄错下标,仔细看初始化才能不搞错。
类是一个域,类的所有成员都在类的作用域中。在类外定义成员函数 ,需要用"::"域作用限定符指定域。
用类创建对象,称为类的实例化。
访问类的成员:
1.通过对象访问类的公有成员
2.通过地址访问公有成员
class A
{
public:
void Func()
{
cout << "hh" << endl;
}
//private:为了方便查看,先用public吧
int _a;
};
int main()
{
A a1;
a1._a = 1;
a1.Func();
A a2;
a2._a = 2;
a2.Func();
return 0;
}
a1和a2的_a地址不一样,但是a1,a2调用的函数是同一个函数(函数定义好,谁来调都是调这个函数)。数据存储到每个对象里,函数地址默认就放到公共代码区,对象里面连公共区域地址都不用存。
常量区或者叫代码段,常量和编译后的代码指令地址都存在这里面。对于编译器来讲,放在常量区就是常规操作,根本不用再另外存函数地址来找函数。
所以说类的大小只计算成员变量的就行,对象里不存函数地址。算的方法跟结构体一样,内存对齐(前面写过博客哦)。
类的大小可以sizeof(类名),也可以sizeof(对象)
类里面没有成员变量,类的大小是多少?
不是0,是1字节。(确实没想到)。这一个字节是为了标识对象存在,不存储数据。不存数据也得开个空间,好取地址,不然我们的对象能是空气对象吗。
class A1
{
public:
void Func()
{
cout << "hh" << endl;
}
};
class A2
{
};
int main()
{
A1 a1;
cout << sizeof(a1) << endl;
A2 a2;
cout << sizeof(a2) << endl;
return 0;
}
既然相同类,不同对象调用的函数是同一个,那为什么同类型的两个对象调同一个函数没传参也会出现不同结果?
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;
d1.Init(2022, 10, 12);
d1.Print();
Date d2;
d2.Init(2022, 10, 13);
d2.Print();//没传参奥
return 0;
}
拿上面的Print举例
1.编译器会对Print函数进行处理,会给函数加一个隐藏的指针参数——this指针。
this是一个关键字。
谁调函数就传的是谁的this地址,就能访问到对象的成员变量。
2.this指针的定义和传递都是由编译器完成,我们不能操作,但是我们可以在类里面用this指针。(一般不在函数形参写出来,但是能在函数里面用)
3.this指针的类型:类名*const this。
复习一下const:
const在*左边,保护指针指向对象的内容不被修改。const在*右,指针指向的对象不能改变。
- const Data* p1;//const修饰*p1
- Data const* p2;//const修饰*p2
- Data*const p3;//const修饰p3
this指针存在哪?
this指针是形参。一般存在栈帧里面。vs进行了优化,使用ecx寄存器传递。
this指针可以为空吗?
先看例子:猜想运行结果(编译报错,运行崩溃,正常运行)
class A
{
public:
void Print()
{
cout <<"Print()"<< endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();//实质:Print(p)。空指针可以传递。成员函数的地址在常量区,不发生解引用空指针
return 0;
}
class A
{
public:
void Print()
{
cout << _a << endl;//实质上是这样访问_a的:p->_a。空指针解引用会运行崩溃。
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();//Print(p)
return 0;
}
总结: 空指针可以传递,但不能解引用。