系列文章目录
文章目录
一、升级struct
在C语言中struct结构体只能定义变量,而在C++中struct被升级为类,不仅能定义变量还可以定义函数,并且向前兼容。
在C语言中,struct加上标签名才可以定义一个变量。而在C++中,struct被升级为类,其后的标签名就可以单独定义一个对象。
C中:
struct tag
{
int a;
int b;
};
int main()
{
//struct和标签名一起定义变量
struct tag A;
return 0;
}
C++中:
struct tag
{
int a;
int b;
};
int main()
{
//标签名单独定义对象
tag A;
return 0;
}
使用升级的struct实现顺序表
#include<iostream>
using namespace std;
struct SeList
{
int* _a;
int _top;
int _capacity;
void Init(int capacity = 4)
{
_capacity = capacity;
_a = (int*)malloc(sizeof(int)*_capacity);
_top = 0;
}
void Push(const int& data)
{
if(_top == _capacity)
{
int newcapacity = _capacity * 2;
int* newarr = (int*)realloc(_a, sizeof(int) * newcapacity);
if (!newarr)
{
perror("realloc fail!");
exit(-1);
}
_a = newarr;
_capacity = newcapacity;
}
_a[_top++] = data;
}
void Pop()
{
_top--;
}
void Destroy()
{
_a = nullptr;
_top = _capacity = 0;
}
void Print()
{
int i = 0;
for (i = 0; i < _top; i++)
{
cout << _a[i] << ' ';
}
cout << endl;
}
};
int main()
{
SeList list;
list.Init(4);
list.Push(1);
list.Push(2);
list.Print();
list.Pop();
list.Print();
list.Destroy();
return 0;
}
二、C++的class
1.类的定义
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者
成员函数。
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
类有两种定义方式:
1.声明和定义全部在类体中;
class Person
{
private:
char* _name;
char* _sex;
int _age;
public:
void ShowInfo()
{
cout << _age << ' ' << _name << ' ' << _sex << endl;
}
};
2.声明与定义分离,声明在类体内而定义在类体外,此时在定义是,函数名前要加类名和作用域限定符,注意是函数名前而不是返回值类型前;
class Person
{
private:
char* _name;
char* _sex;
int _age;
public:
void ShowInfo();
};
void Person::ShowInfo()
{
cout << _age << ' ' << _name << ' ' << _sex << endl;
}
命名习惯:我们常在成员变量名前加后缀或者在其后加后缀,以便与形参做区分。
2.三大访问限定符
public protected private
- public修饰的成员在类外可以直接被访问
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- 如果后面没有访问限定符,作用域就到 } 即类结束。
- class的默认访问权限为private,struct为public(因为struct要兼容C)
三、类的实例化——对象
类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。
而类可以用来实例化出多个对象,而对象占有实际的物理存储空间,用来存储类的成员变量。
那么我们会疑惑,为什么对象的空间只存储成员对象?类的成员函数又被存放在哪里
实际上,在c++语言的设计中,对象中只存储成员变量,而成员函数被存储在公共的代码段。所以对象的空间大小只包括成员变量。因此在计算对象所占大小时,只考虑成员变量并遵守内存对齐队则。
内存对齐规则如下:
- 第一个成员在与结构体偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8 - 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
// 类中既有成员变量,又有成员函数
class A1 {
public:
void f1(){}
private:
int _a;
};
// 类中仅有成员函数
class A2 {
public:
void f2() {}
};
// 类中什么都没有---空类
class A3
{};
上述代码的对象所占空间分别为4,1,1
A1类的对象只存储一个int型变量,根据内存对齐规则,其大小为4个字节;
A2类的对象内部只有成员函数,而成员函数存储在代码段,而不在对象内,所以对象大小应该为0,但是为了使系统意识到该对象的存在,编译器会为这个对象分配一个占位符作为标识,所以最终大小为1个字节;
A3类的对象因为该类为空类,所以应该为0,但出于占位的目的,被分配了一个占位符,最终大小为1 个字节。
四、this指针
1.this指针的引出
为了更好地观察到this指针的引出,我们先实现一个Date类
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(2022, 1, 11);
d2.Init(2022, 1, 12);
d1.Print();
d2.Print();
return 0;
}
对于上述类,有这样的一个问题:
Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函
数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏
的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”
的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编
译器自动完成。
2.this指针的特性
- this指针的类型:类类型* const,即成员函数中,不能给this指针赋值,以此来保护当前对象的地址。
- 只能在“成员函数”的内部使用
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给
this形参。所以对象中不存储this指针。
this指针存在于栈帧中,并且this指针可以为空。即使我们未创建对象也可以获得this指针。
该代码可正常运行。虽然p是空指针未指向实际存在的对象,但仍可以获得存在于栈帧中的this指针。因为this指针不位于对象中,即使不存在实际对象仍可以拿到this指针,只是此时的this指针为空。
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
该代码会运行崩溃,它和上述代码唯一的差别便在于Print()函数内打印了成员变量。而想要访问成员变量就需要解引用this指针,而this指针为空,所以运行崩溃。
class A
{
public:
void Print()
{
//此时打印成员变量
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传
递,不需要用户传递。