C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
C++是基于面向对象的,关注的是对象,将一件事拆分成不同的对象,靠对象之间的交互完成。
目录
一、类的定义
二、访问限定符及封装
一、访问限定符
2、封装
三、类的实例化
四、类对象模型
1、如何计算类对象大小
五、this指针
相关面试题:
C++将C语言里的里的结构体升级成了类,在C++中,结构体不仅可以定义变量,还可以定义函数。我们在数据结构里写的栈就可以把初始化,入栈,出栈等函数都放到结构体里。
但是,C++更喜欢将其写成类。
下面我们来介绍一下类的定义:
class classname
{
//类体:由成员函数和成员变量构成
};//注意有分号
class为关键字,classname为类名,{}中为类的主体,注意有分号。
类的两种定义方式:
1、将声明和定义都放在类体中。
class Person
{
public:
void showInfo()
{
cout << _name << "-" << _sex << "-" << _age << endl;
}
public:
char* _name;
char* _sex;
int* _age;
};
2、将类的声明放.h文件中,将类定义放.cpp文件中(推荐)。
person.h文件中:
class Person
{
public:
void showInfo();
public:
char* _name;
char* _sex;
int* _age;
};
.cpp文件中:
#include "person.h"
void Person:: showInfo()
{
cout << _name << "-" << _sex << "-" << _age << endl;
}
访问限定符有3种:
public | 公有,在类外可以直接被访问。 |
protected | 保护,类外不能直接访问。 |
private | 私有,类外不能直接访问,class默认为private。 |
说明:访问权限作用域从该访问权限出现的位置开始直到下一个访问限定符出现时为止。
如果后面没有访问限定符,则到}为止。
再来看这样一段代码:
class Date
{
public:
void Init(int year)
{
//这里的year到底是成员变量,还是函数形参?
//其实是成员变量,因为局部优先
year = year;
}
private:
int year;
};
为了避免上面这种情况,通常在成员变量前建议加上前缀_来区分。
注意:
1、如果要将类里的函数定义成内联函数,不能将声明和定义分里;同时,在C++里规定,在类里定义的函数默认为内联函数(如果函数很长,其实也不会被定义成内联函数,决定权在编译器)。
2、访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。
访问限定符只在编译时有用是因为它们的主要作用是帮助程序员在编写代码时遵循良好的编程实践,以确保数据的安全性和可维护性。访问限定符可以防止程序员意外地修改或访问不应该被访问的数据,从而提高代码的健壮性和可读性。
然而,在数据映射到内存后,访问限定符就没有任何区别了。这是因为内存中的数据只是一堆字节,没有任何关于其类型或访问权限的信息。因此,即使一个变量被声明为私有或公共,它们都会被存储在相同的内存位置,并且可以通过相同的方式进行访问。
所以说,虽然访问限定符在代码编写过程中非常重要,但一旦程序运行起来并将数据映射到内存中,它们就不再起作用了。
面向对象的三大特性:封装、继承、多态。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
封装本质是一种管理,让用户更方便使用类。
C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。
概念:用类类型创建对象的过程,称为类的实例化。
class Person
{
public:
void showInfo();
public:
char* _name;
char* _sex;
int* _age;
};
void Test()
{
Person._age = 100;//编译失败:error c2059:语法错误:“.”
Person man;//man是类Person实例化出的对象,占用实际的物理空间,存储成员变量。
man.showInfo();
}
一个类的大小,实际就是该类中“成员变量”之和
计算类的大小是不计成员函数的,其余计算方法与结构体类似,遵循内存对齐原则。
注意:若类中只有成员函数,或者类为空类,则类大小为1byte,是为了占位,表示对象存在,不存储有效数据。
往期博文链接:
结构体大小计算https://blog.csdn.net/m0_73065213/article/details/128637954?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168294275316800217238141%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=168294275316800217238141&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-128637954-null-null.article_score_rank_blog&utm_term=%E7%BB%93%E6%9E%84%E4%BD%93&spm=1018.2226.3001.4450
类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小。
每个对象中只保存成员变量,成员函数存放在公共的代码段。
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(2023, 5, 1);
d2.Init(2023, 5, 2);
d1.Print();
d2.Print();
return 0;
}
对于上面这段代码,为什么两次调用Print函数, 是在公共区域调用同一个函数,却有不同的输出结果呢?这里我们就得介绍一下this指针了。
C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
注意:this不能在形参和实参中显示传递,但可以在函数内部显示使用。
1、this指针存在哪里?对象里面?栈?堆?静态区?常量区?
答案是:存在栈里,因为this是形参,所以this就跟普通参数一样存在函数调用的栈帧里的。
2、this指针可以为空吗?
下面两个程序的运行结果是什么?
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;
}
第一个:正常运行
第二个:运行崩溃
why?
对于第一个程序,p调用Print,不会发生解引用,因为Print的地址不在对象中,p会作为实参传递给this指针。this指针随为空,但第一个程序没有对this指针解引用,所以不会运行崩溃。
第二个程序本质上是
cout << this->_a << endl;
所以此时this为空就会出问题。