目录
本篇文章主要讲述
类
实例化
this指针
等问题
一. 类
1. 类的定义
如以下代码
class为定义类的关键字,Stack是类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。类体中的内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的方法或者成员函数。
#include<iostream>
using namespace std;
class Stack
{
public:
//成员函数
void StackInit(int n=4)
{
arr = (int*)malloc(sizeof(int) * n);
capacity = 0;
Top = 0;
}
void StackPush(int data)
{
if (Top == capacity)
{
int newcapacity = capacity == 0 ? 4 : capacity * 2;
int* tmp = (int*)realloc(arr, sizeof(int) * newcapacity);
if (tmp == NULL)
{
perror("relloc fail");
return;
}
arr = tmp;
capacity = newcapacity;
}
arr[Top] = data;
Top++;
}
int StackTop()
{
return arr[Top - 1];
}
void StackDestroy()
{
arr = NULL;
capacity = 0;
Top = 0;
}
private:
int* arr;
int Top;
int capacity;
};
int main()
{
Stack st;
st.StackInit();
st.StackPush(6);
st.StackPush(9);
cout << st.StackTop() << endl;
st.StackDestroy();
return 0;
}
为了区分成员变量,一般习惯上 成员变量会加一个特殊的标识,并不是C++强制的,具体看企业的要求
如下定义
#include<iostream>
using namespace std;
class Stack
{
public:
int* _arr;
int _Top;
int _capacity;
//int* m_arr;
//int m_Top;
//int m_capacity;
};
C++中struct也可以定义类,C++兼容C中struct的用法,同时struct升级成了类,明显的区别为struct中可以定义函数了,并且在定义中不必须加struct
如下代码所示,既使用了函数又直接用Pc定义了p1
struct Pc
{
public:
void Init()
{
a = 10;
b = 11;
c = 13;
}
int a;
int b;
int c;
};
int main()
{
Pc p1;//struct可以省略
struct Pc p2;
p1.Init();
p2.b = 33;
p2.c = 44;
p2.a = 22;
cout << p1.a << " " << p1.b << " " << p1.c << endl;
cout << p2.a << " " << p2.b << " " << p2.c << endl;
return 0;
}
定义在类里面的成员函数默认为inline
2. 访问限定符
C++一种实现封装的方式,用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。有三种权限
public(公共权限) | 可以在类外直接被访问 |
private(私有权限) | 类外不可以直接访问(继承中子不可以访问父亲私有内容) |
protected(保护权限) | 类外不可以直接访问(继承中子可以访问父亲的安全内容) |
访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止,如果后面没有访问限定符,作用域就到 } 即类结束的时候。
class Pc
{
public:
void Init()
{
a = 10;
b = 11;
c = 13;
}
private:
int a;
int b;
int c;
};
int main()
{
Pc p2;
p2.Init();
p2.b = 33;
p2.c = 44;
p2.a = 22;
cout << p2.a << " " << p2.b << " " << p2.c << endl;
return 0;
}
我们发现上述代码编译器会报错,因为调用了私有的成员变量,但是调用Init函数是可以的,因为函数处于public作用域下
class定义成员没有被访问限定符修饰时默认为private,struct定义成员没有被访问限定符修饰时默认为public
如以下代码所示
#include<iostream>
using namespace std;
class Pc1
{
int a;
int b;
int c;
};
struct Pc2
{
int a;
int b;
int c;
};
int main()
{
Pc1 p1;
Pc2 p2;
p1.b = 33;
p1.c = 44;
p1.a = 22;
p2.b = 33;
p2.c = 44;
p2.a = 22;
cout << p1.a << " " << p1.b << " " << p1.c << endl;
cout << p2.a << " " << p2.b << " " << p2.c << endl;
return 0;
}
我们发现再给p1中的成员赋值时会报错
一般成员变量都会被限制为private/protected,需要给别人使用的成员函数会变为public
3. 类域
类定义了一个新的作用域,类的所有成员都在类的作用域中,在类外定义成员时,需要使用:: 作用域操作符指明成员属于哪个类域。
如下代码所示
#include<iostream>
using namespace std;
class Pc1
{
int a;
int b;
int c;
public:
void Init();
void Printf();
};
void Pc1::Init()
{
a = 11;
b = 12;
c = 13;
}
void Pc1::Printf()
{
cout << a << " " << b << " " << c << endl;
}
int main()
{
Pc1 p1;
p1.Init();
p1.Printf();
return 0;
}
以上代码两个函数都是声明在类里,定义在类外用::域作用限定符指明了成员属于哪个类域。
如果上述代码不指名呢?
类域影响的是编译的查找规则,上面程序中如果不指定类域Pc1,那么编译器就会将Init与Printf当成全局函数,那编译时就找不到a,b,c等成员的声明/定义在哪,就会报错。指定类域Pc1,就知道了Init与Printf是成员函数,当前域中找不到的成员就会到类域中去查找。
二. 实例化
1. 实例化的概念
用类 类型在物理内存中创建对象的过程,称为类实例化出对象。
#include<iostream>
using namespace std;
class Pc
{
//这里只是声明并没有开空间
int a;
int b;
int c;
public:
void Init()
{
a = 11;
b = 12;
c = 13;
}
void Printf()
{
cout << a << " " << b << " " << c << endl;
}
};
int main()
{
Pc p;//Pc实例化出对象p
//没有空间不能直接访问
//Pc::a;
//Pc::b;
//Pc::c;
return 0;
}
上述代码中 在Pc类实例化出对象p是才为Pc中的成员开辟了空间。类就类似一张图纸,可以根据图纸修房子住人,但是其本身不能住人
2. 对象的大小
类实例化出的每个对象,都有独立的数据空间,所以对象中肯定包含成员变量,那么成员函数是否包含呢?函数被编译后是一段指令,对象中没有办法存储,这些指令存储在一个单独的区域(代码段),那么对象中非要存储的话,只能是成员函数的指针。而函数指针是不需要存储的,函数指针是一个地址,调用函数被编译成汇编指令 call地址,其实编译器在编译链接时,就要找到函数的地址,不是在运行中找,只有动态多态是在内存中找,就需要存储函数地址,这个暂且先不讲
假设同一个类Pc
int main()
{
Pc p1;
Pc p2;
p1.Init();
p2.Init();
p1.Printf();
p2.Printf();
return 0;
}
我们通过观察反汇编代码可得
在反汇编代码中,call指令通常用于跳转到其他内存地址执行代码。
我们发现两个所跳向函数地址相同
C++规定实例化的对象也要符合内存对齐规则
内存对齐规则
1.第一个成员对齐到结构体变量为偏移量0的地址处
2.其他成员要对齐到某个数字(对齐数)的整数倍地址处
对齐数:编译器默认的一个对齐数与该成员占内存之间的较小值 vs默认对齐数为8Linux中gcc没有默认对齐数,对齐数就是成员自身大小
3.结构体的大小为最大对齐数(占用字节最多的成员变量)的整数倍(每个成员变量都有一个对齐数)
4.嵌套结构体的情况:嵌套结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
#include<iostream>
using namespace std;
class S1
{
char _a;
int _b;
char _c;
public:
void Init()
{
_a = 11;
_b = 12;
}
void Printf()
{
cout << _a << " " << _b << " " << endl;
}
};
class S2
{
char c1;
char c2;
int i;
public:
void Printf()
{
cout << "1111" << endl;
}
};
class S3
{
};
int main()
{
S1 p1;
S2 p2;
S3 p3;
cout << sizeof(p1) << " " << sizeof(p2) << " " << sizeof(p3) << endl;
return 0;
}
模拟一下S1与S2在内存中分别怎么存储的
而S3没有成员变量但是其对象的大小为1,为什么呢?
因为若一个字节也不给,没法表示对象存在过,这里给1字节为了表示对象存在过
三. this指针
使用
#include<iostream>
using namespace std;
class Pc
{
char _a;
int _b;
char _c;
public:
void Init(int aa = 11, int bb = 12, int cc = 13)
{
_a = aa;
_b = bb;
_c = cc;
}
void Printf()
{
cout << _a << " " << _b << " " << _c << endl;
}
};
int main()
{
Pc p1;
Pc p2;
p1.Init();
p2.Init(1, 2, 3);
p1.Printf();
p2.Printf();
return 0;
}
Pc类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用Init和 Print函数时,该函数是如何知道应该访问的是p1对象还是p2对象呢?那么这⾥就要看到C++给了 ⼀个隐含的this指针解决这里的问题
编译器编译后,类的成员函数默认都会在形参第⼀个位置,增加⼀个当前类类型的指针,叫做this 指针。⽐如Pc类的Init的真实原型为, void Init(Date* const this, int year, int month, int day)
void Init(Date* const this, int aa = 11, int bb = 12, int cc = 13);
类中的成员函数访问成员变量,其本质都是通过this指针进行访问,如上述代码Init赋值实际操作为
void Init(int aa = 11, int bb = 12, int cc = 13)
{
this->_a = aa;
this->_b = bb;
this->_c = cc;
}
注意
C++规定不能在实参和形参的位置显式的写this指针(编译时编译器会处理,如下图所示),但是可以在函数体内显式使⽤this指针。
这篇文章就到这里啦,喜欢可以点赞支持
(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤
标签:p2,对象,void,int,Init,对齐,成员 From: https://blog.csdn.net/m0_68142120/article/details/140594121