目录
1.类的引用
C语言struct结构体中只能定义变量,在c++中,结构体内不仅仅可以定义变量,也可以定义函数。比如:C语言实现数据结构的栈结构体只能定义变量,现在以c++方式实现,可以在结构体内定义函数。
typedef int DataType;
struct Stack
{
void init()
{
DataType* tmp = (DataType*)malloc(4 * sizeof(DataType));
if (tmp == nullptr)
{
perror("malloc fail");
return;
}
_nums = tmp;
_size = 0;
_capacity = 4;
}
void destroy()
{
if (_nums)
{
free(_nums);
_nums = nullptr;
_size = 0;
_capacity = 0;
}
}
void push(int x)
{
if (_size == _capacity)
{
DataType* tmp = (DataType*)realloc(_nums, 2 * _capacity * sizeof(DataType));
if (tmp == nullptr)
{
perror("realloc fail");
return;
}
_nums = tmp;
_capacity = 2 * _capacity;
}
_nums[_size] = x;
_size++;
}
bool empty()
{
if (_size == 0)
return true;
else
return false;
}
void pop()
{
if (!empty())
{
_size--;
}
}
DataType top()
{
if (!empty())
{
return _nums[_size - 1];
}
else
{
return INT_MIN;
}
}
//...
DataType* _nums;
int _size;
int _capacity;
};
c++兼容c中的struct的用法,c++升级struct为类,在c++中更喜欢用class来替换;
2.类的定义
class className
{
//类体:由成员变量和成员函数组成
};
class为定义类的关键字,className为类的名字,{}中为类的主体,注意类定义结束时后面的分号不能省略;
类体中:类体中的变量称为类的属性或成员变量; 类体中的函数称为类的方法或成员函数;
类的两种定义方式:
1.声明与定义不分离,全部在类体中(注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理)
class A
{
public:
void func()
{
cout << "func()" << endl;
}
private:
int _a;
int _b;
};
2.声明与定义分离,类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::来指明类域
一般情况下,用第二种方式将声明与定义分离;
成员变量一般加个前缀或者后缀标识区分:
3.类的访问限定符
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
4.封装
面向对象的三大特性:封装、继承、多态
先说封装:
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
5.类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中,在类体外定义成员时,需要使用:: 作用域操作符指明成员属于哪个类域,例如类的成员函数声明与定义分离。
6.类的实例化
用类 类型创建对象的过程称为类的实例化
2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
定义是在对象的实例化的时候,此时会开空间
7.类对象模型
7.1 类对象的大小
同一个类实例化的多个对象,每个对象的成员变量都是对象自己的,成员函数都是公共(公共代码区/代码段)的,所以计算一个类的大小是只计算成员变量的内存对齐后的大小
对象的占用的大小,只考虑成员变量(内存对齐)
7.2 结构体内存对齐规则
特殊:空类(或类中只有成员函数)的大小--是1
定义的核心是开空间,空类大小是一个字节,不存储有效数据,标识对象被定义出来(大小是0就没有开空间,就不存在定义了)
8.this指针
8.1 this指针的引用
先看代码:
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;
d1.Init(2024, 5, 20);
Date d2;
d2.Init(2024, 5, 19);
d1.Print();
d2.Print();
return 0;
}
可能会有人疑惑:Date类中的Init和Print成员函数,函数体中没有关于不同对象的区分,所有对象共用一个函数,那当d1和d2调用函数时,函数怎么知道应该设置d1对象和d2对象呢?
8.2 this指针的特性
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
this指针是隐藏在第一位的形参
//void Init(Date* const this, int year, int month, int day)
//{
// this->_year = year;
// this->_month = month;
// this->_day = day;
//}
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
由特性3:this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。可知,p->Print()实际上是将nullptr传给了Print函数,并没有对nullptr进行访问(传一个空指针没有问题),Print函数内部也没有对this(nullptr)进行访问,所以整体下来不会报错,选C
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void Print()
{
cout << "Print()" << endl;
cout << _a << endl;;;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
Print对this(nullptr)进行了访问,对nullptr进行访问会崩溃,选B