什么是类与对象?
在此之前,我们先了解:
面向对象与面向过程的认识:
我们学习过的C语言:面向过程,C++:面向对象
以洗衣服时的例子:
面向过程:看重的是怎么样洗衣服的动作:扭,戳,拉等等
面向对象:看的是有哪些物体:人,衣服,衣架等等。
一:类
什么是类?
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:之前用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数
//struct Date
class Date
{
//函数
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
int _year;
int _month;
int _day;
};
上面结构体的定义,在C++中更喜欢用class来代替
类的定义:
class+类的名字
{
//成员函数
//成员变量
}; //;不可以省略!!!
class为定义类的关键字
类的两种定义方式:
1. 声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内
联函数处理。
2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
ps:(我们一般建议使用第二种,除了平常的一般练习)
class Date
{
public:
void Init(int year,int month,int day)
{
year = year;
month = month;
day = day;
}
private:
int year;
int month;
int day;
};
像上面的代码,我们虽然能够编译得过去,但是,看着是不是很生硬难看?
这里的year,month,day到底是成员变量,还是函数形参?是不是很难看懂?
所以我们一般会为了区分它,会在成员变量前+--->"_"来区分:
class Date
{
public:
void Init(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
ps:其他方式也是可以的,看个人要求。
另外,我们会发现上面代码为啥会public,private呢?它们又是什么意思呢?
访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用
访问限定符:public(公开),private(私),protected(保护)
【访问限定符说明】
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class的默认访问权限为private,struct为public(因为struct要兼容C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
C++中struct和class的区别是什么?
C++需要兼容C语言,所以C++中struct可以当成结构体使用。另外C++中struct还可以用来定义类。和class定义类是一样的,区别是struct定义的类默认访问权限是public,class定义的类默认访问权限是private。
封装
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。控制哪些方法在类外部之间可以使用。
举个例子:
我们的电脑,这样一个复杂的设备,提供给用户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。这样封装的意义就是为了更方便
5.类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 ::作用域操作符指明成员属于哪个类域。
class Date
{
public:
void Init(int year, int month, int day);
private:
int year;
int month;
int day;
};
void Date::Init(int year, int month, int day)
{
year = year;
month = month;
day = day;
}
类的实例化
用类类型创建对象的过程,称为类的实例化
1.类是对对象进行描述的,像模型一样的东西,限定了哪些类的成员,而定义出的类它不会具体地存储空间。
就像房子的图纸一样,类像是图纸,不是具体的实物,所以不会占用空间。
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date._year = 100; //错误的
return 0;
}
上面,用具体的话来讲,就是图纸里面想要住人的感觉。
2.一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
比如,接上面代码,我们可以实例化多个对象:
int main()
{
Date d1;
Date d2;
return 0;
}
7.1 如何计算类对象的大小
我们通过上面知道,我们的类成员有:成员函数和成员变量。
那么在计算大小的时候,它又是怎么计算的呢?
结论:一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐
这里有具体的内存对齐的讲解:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
*VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
————————————————
注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象
3. 什么是大小端?如何测试某台机器是大端还是小端,有没有遇到过要考虑大小端的场景
结构体,枚举,联合知识点笔记总结-优快云博客
8.this指针
class Date
{
public:
void Init(int year=1, 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;
};
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
其实,编译器会在“非静态的成员函数中生成一个隐藏的指针变量,就是this指针。在函数体中所有“成员变量”的操作,都是通过该指针去访问,只是使用的人不用去传递。
特性:
1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
2. 只能在“成员函数”的内部使用
3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给
this形参。所以对象中不存储this指针。
4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,
不需要用户传递
5.虽然我们不能在在形参中看到this指针,但是我们可以这样子观察:(但是我们平常写的时候一般不会写,这里只是刚开始了解,好让我们观察this指针而已)
class Date
{
public:
void Init(int year=1, int month=1, int day=1)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
void Print()
{
cout <<this->_year <<this->_month << this->_day << endl;
}
private:
int _year;
int _month;
int _day;
};
下面
第一个会出现问题吗?
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
第二个呢?
class A
{
public:
void PrintA()
{
cout<<_a<<endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
return 0;
}
因为首先呢,就是我们这个成员函数,它是不会包括在你的类实例化对象中的。而真正占据你这个类实例化对象的空间,应该是它的成员变量。
所以说,即便你这个P它是一个空指针,把P去调用它里面的print函数,那P就是作为这个print函数里面的第一个参数的this指针。那么,此时你这个print函数里面并没有调用成员变量,所以说这个this指针根本就无。就没有去访问,也就是没有解应用,所以就不会去报错。
而像你第二个,你看那个实例,就是你那个P是空指针嘛,然后P跟this是等价的,你的this指针也是个空指针,但是你在第二个PrintA函数里面,你去访问了它的。_a这个成员变量,所以说就相当于this指向的_a成员,那么就是发生了空指针的禁用,也就是造成了非法内存的访问。运行崩溃
到了本次鸡汤环节:
一旦有了意志,脚步也会轻松起来。