面向对象和面向过程
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成 就拿外卖来举例,C语言关注的是外卖的点餐,送餐。 而C++关注的是点餐的人,送餐的人之间的联系。
类的引入
我们知道在C语言中有结构体这种结构 strutc student { int a; string name; }; 在结构体里我们声明变量。 从前在C语言中定义一个结构体的对象需要这样: struct student st; 但是在C++中,student st;便可以定义一个对象了。当然前面的那种写法也是可以的。所以说C++兼容了C语言的所有语法与特性。 C++中的类是一种与结构体相似的结构。多称做class。 如,创建一个具有姓名,学号的学生类: class student { string name; int num; };
类的定义
class className { // 类体:由成员函数和成员变量组成 }; // 一定要注意最后有个分号 class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号。 类中的元素称为类的成员:类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数 类有两种定义方式: 1.声明和定义都放在类内 如学生类: class student { string showname() { return name;//(返回学生姓名) } int shownum() { return num;//(返回学生的学号) } string name; int num; }; 2.声明和定义分文件(头文件声明,cpp文件定义) 这里需要注意,在.cpp文件对函数进行定义的时候,需要指明函数的作用域,否则编译器不知道这个函数是哪里来的。
类的访问限定符
类有三种访问限定符: 访问限定符的说明:
- public修饰的成员在类外类内都可以直接被访问。
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的),类内可以访问。
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
对于上面的3点,分别举例说明: 1. public修饰的成员在类外类内都可以直接被访问。
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的),类内可以访问。 这里protected也是如此,就不进行展示了 3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止 下图中的showname属于public权限。 private后没有权限访问符了,所以其后面的都属于private权限。
结构体与类的区别
在结构体中,只能定义变量,在C++中,类进行了升级,类既可以定义变量,又可以定义函数: >区别1:类内可以定义函数与变量,而结构体只能定义变量。 如上面的student 类,我们为其定义了一个输出姓名的函数 class student { void name() { cout<<name<<endl; } string name; int num; }; >在很多C++的书中,类的函数又称作成员函数/方法,变量又称作成员/属性。所以我们在定义变量的时候,常这样定义如:int m_+变量名称,这里的m就是member(成员)的缩写。 区别二:默认访问权限不同 类的默认访问权限是:private 结构体的默认访问权限是:public
类的三大特性之—封装
类有三大特性:封装—继承—多态 继承和多态在后面的博客会再进行说明。 封装:C++通过类将函数和变量封装在一个类里,并通过访问限定符对其进行严格管理,这就是封装 封装的好处:在封装下,方法和成员处于同一个空间中,和c语言不同,c语言中函数和变量是凌乱的,这样使得数据可以被直接进行访问,可能会造成数据的污染。而C++的访问限定符可以避免这一情况,类的设计可以将函数接口公开出来,其成员设计为私有,使得只能通过函数接口对数据进行访问与修改。这样做使得代码更好得到管理,安全性也更高。
类的实例化
用类创建对象的过程,称为类的实例化 类只是一个模型,定义了一个类时,编译器并没有为其分配空间,只有当类定义出对象以后,编译器才会为类分配空间。 举例来说: 类的定义就像是图纸,而对象则是根据图纸设计出来的房子。
类的对象模型
先看一段代码:计算下面的对象占多少字节。
class student {
public:
int shownum()
{
return m_num;
}
int m_num;
};
int main()
{
student s1;
cout << sizeof(student) << endl;
cout << sizeof(s1) << endl;
}
结果都是4。 注意:这里的sizeof(s1)和sizeof(student)计算的都是一个对象的大小,类是不分配空间的。 这里有同学可能会认为是8,因为前面还有一个函数。答案显然说明了函数不属于这个类。那么函数属于哪里呢? 这样试想:成员函数如果存储在对象中,但是对于类的对象来说,其函数都是实现同样的功能,对象中存储函数就会造成大量的空间浪费。C++的设计者在设计类的时候想到了这一点,因为不同对象具有不同的数据/成员,所以对象内只存储变量。而成员函数则存储在公共代码区中,供所有对象使用。 如下图所示: C++空类占多少字节? 这里设计了一个student空类,因为成员函数不属于类。 这里计算出来的结果都是0。 这里明明没有成员变量,为什么是0呢? 这样试想:通过类创建出来一个对象,这个对象是不是就有其独立的内存空间,不同的对象处于不同的空间中,有其独立的内存地址。C++为了辨别不同的对象,即使是空类,C++都会为其分配一个字节大小的空间进行区别。
this指针
看下面这段代码,Setdate中的year是成员变量还是形参year呢?
class Date
{
public:
void SetDate(int year, int month, int day)
{
year = year;
}
private:
int year;
};
int main()
{
Date d1;
d1.SetDate(2018);
}
根据就近原则,这里的year是形参中的year。 如果要让这个year是成员变量的year,又要怎么修改呢? 1.作用域: 声明year的作用域是属于Date类的。 Date::year=year;
2.this指针 将year=year改成this->year=year。这时第一个year就是成员变量,第二个year就是形参。 为什么加了this指针C++就能分辨形参和成员变量了?C++在此又做了哪些优化呢? 在此之前,我们先了解一下类外访问成员变量和成员函数的方式
1.访问成员变量:
在访问成员变量的时候,通常都是使用 对象名.成员变量名的方式进行访问。 在访问成员变量的时候,对象. 的意思就是告诉编译器要去对象里面找,而成员变量名则是告诉编译器具体要找的成员变量的名字。 如日期类的d1.year, d1.就是告诉编译器这个成员变量属于d1这个对象,而year就是要去d1对象里寻找year这个变量。
2.访问成员函数
在访问成员函数的时候,和访问成员变量的格式基本是一样的,但是含义并不同。 这里我们需要知道,其实在类的非静态成员函数都隐藏了一个参数,这个参数的类型是类名+const *this,这也就是所谓的this指针。我们通过 类名.成员函数名 调用函数的时候,编译器会在实参部分自动传进对象的地址。而成员函数通过this指针对该对象的地址进行解引用操作,就可以知道哪个对象在调用这个函数了。 我们以Date类为例: 在函数内部访问成员变量的时候,其实都是通过解引用this指针找到这个变量,然后进行访问。在没有形参同名的情况下(非静态成员函数内的成员变量前面都会自动加上this,所以我们一般不在变量前+this进行访问)
还有一个需要非常注意的点:这里的形参对象的地址和实参this指针编译器会自动替我们写上,不能人为传递这两个参数。
this指针的特征: 1.this指针只能在成员函数内部进行使用 2.不能人为在成员函数内部改变this指针的指向 3.this指针是非静态成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
this存在哪
this是形参,一般是存储在栈上。 当然,有时候也会存储在寄存器上,因为寄存器的存取速度更快,具体还是得看编译器。 我的vs编译器下this指针是存储在寄存器里。
this指针面试题
下面程序运行的结果:
1.编译报错
2.运行崩溃
3.正常运行
class A
{
public:
void show()
{
cout << "show()" << endl;
}
};
int main()
{
A *a = nullptr;
a->show();
}
让我们分析下这题 1.编译阶段检查的是语法的错误,这里没有语法错误,所以不会报错 2.这里虽然对象a是一个空指针,但是我们并没有解引用这个空指针,所以运行也不会崩溃。 所以本题的答案是正常运行。
接着再看一题
下面程序运行的结果:
1.编译报错
2.运行崩溃
3.正常运行
class A
{
public:
void show()
{
cout <<m_year<< endl;
}
int m_year;
};
int main()
{
A *a = nullptr;
a->show();
}
1.和第一题一样,没有任何语法错误,因此排除第一个答案 2.在成员函数内部访问成员变量的时候,成员变量前会自动加上this,这里的m_year其实是this->year,但是这里指针a是一个空指针,this解引用该指针的地址就相当与解引用空指针。解引用空指针会导致程序崩溃。 因此本题答案为第二个。
|