类和对象(上)
前言
学习了前面的C++入门。我们才能更好的理解类和对象这一章,如果没看过C++入门的兄弟们,请移至我主页中的C++入门和C++入门(下)去学习。只有学习了前面的知识,今天的知识才能更好的理解
一、面向对象和面向过程的初步认识
C语言是面向过程的,关注
的是过程
,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的
,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成
面向对象的三大特征,封装、继承、多态。当然在实际解决问题中,实际不止这三种
二、类–定义出一个新的类型
类由两部分构成:
- 成员变量(类的属性)
- 成员函数(类的方法)
那怎么定义一个类呢?
一般我们用class
关键字来定义类。但同时我们可以用struct
来定义类,C++不仅兼容C的所有用法,同时C++中把struct
升级为了可以定义类的关键字
1.类的定义
class className
{
//类体
};//注意这个分号
类的两种定义方式
-
声明和定义全部放在类体内
注:
成员函数如果在类中定义,编译器可能将其当作内联函数处理。 -
声明放在(.h)中,函数在(.cpp)中定义。
一般采用第二种。
在类中成员变量是声明,一般声明类后不占用空间,只由实例化后才被定义,占用空间。也就是在运用类时,其才占用空间,才被定义。
2.访问限定符
C++实现封装的方式
用类将对象的属性与方法结合在一起,让对象的属性与方法结合在一起,让对象更加完善,通过访问权限选择性的将其接口给外部用户使用
访问限定符分为三类
- 共有(public)
- 保护(protecten)
- 私有(private)
对访问限定符说明
public
修饰的成员在类外可以直接被访问protecten
和private
修饰的成员在类外不能直接被访问- 访问权限作用域从访问限定符出现的位置开始到下一个限定访问符出现时为止
class
的默认访问权限为private
,struct
默认为public
注
:protect
和private
在我们现在的学习中是类似的,没有区别
访问限定符只在编译时有用,当数据映射到内存时,没有区别
对访问限定符的应用
希望外部访问的,就用公有(public),如成员函数
不希望外部访问的,就用其余两个,如成员变量
注意,private
只限制域外访问,不限制类里面的访问
三、封装–更加严格规范
将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来实现交互
封装的本质是一种管理,如用前面的访问限定符,想给你访问的—设为公有,不想给你访问的—设为私有
四、类的实例化
用类创建对象的过程
定义类并没有分配实际的内存空间
实例化出的对象,占用实际的物理空间
如:
class A
{
private:
int a;
int b;
int c;
public:
void print()
{
cout << "print" << endl;
}
};
//这个时候A不占用内存,就相当于修房子一样,它现在是相当于一个图纸,不占用内存
int main()
{
A a;
//这个时候实例化a后才建好空间
int x = sizeof(a);
cout << x << endl;
//这个时侯打印12,因为函数存在公共代码区。所以不占用空间
return 0;
}
注
:
- 成员函数只存在公共代码区,不占用内存
- 注意内存对齐
总结
成员变量一般是不允许被访问到的,所以要设置初始化函数
- 成员变量很容易与函数中的很多形参同名,为了区分,写成以下形式
int _year; //建议写成这种形式
int myear;
int year_;
一些实例化例子
class A2
{
public:
void f2()
{
.....
}
};
class A3
{};
以上的类实例化后,都占有一个字节。
为什么上述一个空类、一个只含有函数的类,不是占有0个空间,而是占有1个字节呢?
为了区分不同空类,如:
class A2
{
public:
void f2()
{
.....
}
};
class A3
{};
int main()
{
A2 a2;
A2 aa2;
}
如果占有0个字节,编译器怎么区分哪个是a2,哪个是aa2,因为找不到其地址了,所以得占有一个字节,给一个字节,是为了存储数据,占位。
五、this指针(隐含)
1.this指针引出
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()
{
Data d1;
d1.Init(2021,5,20);
Date d2;
d2.Init(2022,5,20);
}
很多兄弟可能就有疑惑了,不是引出this指针么,写这么长一段代码。其实this指针就在其中,记住this指针的特性,隐含。
class Date
{
public:
void Init(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
//编译器会把上面这个函数变成以下模样
void Init(Date* this,int year,int month,int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
//发现问题了吗,对,this指针是我们编译器处理后加上的。其中的内容this->我们可以自己写成这样,也可等编译器处理。
private:
int _year;
int _month;
int _day;
};
int main()
{
Data d1;
d1.Init(2021,5,20);
//这里编译器也会处理成d1.Init(&d1,2021,5,20),对应上面类函数的变化
Date d2;
d2.Init(2022,5,20);
}
那么为什么编译器要加一个this指针,来特殊处理这一步呢?
因为,通过this指针,我们就可以对不同的实例化进行初始化,哪个对象调用成员函数,成员函数中访问的就是哪个对象中的成员变量。
2.this指针的注意
-
this指针是隐含的,是编译器编译时加上的,我们不能显示的在调用和函数定义中加。
-
我们可以在成员函数中使用this指针,
可以打印和访问这个this
。 -
this
指针是形参,其是存储在栈中(一般)不同编译器不同,VS是使用ecx寄存器存储
-
this
是关键字,不能与变量重名
3.面试题
- 下面程序能编译过吗?
- 下面程序会崩溃吗?在哪里崩溃
class A
{
public:
void printA()
{
cout << _a << endl;
}
void show()
{
cout << "show()" <<endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->printA();
p->show();
}
上面程序能访问通过。
但 p->printA();这个语句是错误的,因为他要访问类中的_a,而定义的其为一个空指针,就相当于对空指针的访问
而p->show();正常运行,这里没有对p这个指针解引用,因为show等成员函数的地址没有放在对象里面,存在公共代码区