1.类的引入
先来看看用C和C++创建结构体变量的区别:
//C语言的结构体
struct ListNode1
{
int val;
struct ListNode1* next;
};
//C++的结构体
struct ListNode2
{
int val;
ListNode2* next;
};
int main()
{
struct ListNode1 L1;//C语言的结构体创建变量
ListNode2 L2;//C++的结构体创建变量
}
C++进行了优化,在创建结构体变量时省略了struct关键字。
另一显著区别是在C++的结构体定义中,可以定义变量和函数,而C语言只能定义变量,下面是一个例子:
typedef int DataType;
struct Stack
{
void Init(size_t capacity)
{
_array = (DataType *)malloc(sizeof(DataType) * capacity);
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(const DataType &data)
{
// 扩容
_array[_size] = data;
++_size;
}
DataType Top()
{
return _array[_size - 1];
}
void Destroy()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
DataType *_array;
size_t _capacity;
size_t _size;
};
调用函数的方法也很简单,将函数当作普通变量,用.操作符即可
int main
{
Stack s;
s.Init(10);
s.push(1);
}
结构体中有一个函数
int stackSize()
{
return size;
}
这个函数并没有形参,为什么可以返回size值呢?
在同一个结构体内部的变量,是可以直接被结构体内的函数访问到的。因为size存在于结构体中,所以结构体内的函数可以访问size。
但是为了更好的区别C语言的结构体与C++的结构体,C++倾向于用一种叫做类的结构来替代结构体,用class来代替。
2.类的定义
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。类体中内容称为类的成员;类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
类有两种定义方式:
3.访问限定符
先定义如下的一个类:
class stack
{
void Init()
{
cout << "初始化栈" << endl;
}
void Push(int x)
{
cout << x << "被压栈了" << endl;
}
void Destroy()
{
cout << "销毁栈" << endl;
}
int stackSize()
{
return size;
}
int* a;
int size;
int capacity;
};
这样定义产生了问题,下面写一个主函数
为什么“不可访问”呢?这就引出了访问限定符。
1.public修饰的成员在类外可以直接被访问
2.protected和private修饰的成员在类外不能直接被访问(这方面protected和private是类似的)
3.访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4.如果后面没有访问限定符,作用域就到} 即类结束。
5.class的默认访问权限为private,struct为public(因为struct要兼容C语言)
所以应该将类这样定义
class stack
{
public:
void Init()
{
cout << "初始化栈" << endl;
}
void Push(int x)
{
cout << x << "被压栈了" << endl;
}
void Destroy()
{
cout << "销毁栈" << endl;
}
int stackSize()
{
return size;
}
//private
int* a;
int size;
int capacity;
};
4.封装
面向对象的三大特性:封装、继承、多态。 在类和对象阶段,主要是研究类的封装特性,那什么是封装呢? 封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来 和对象进行交互。 封装本质上是一种管理,让用户更方便使用类。比如:对于电脑这样一个复杂的设备,提供给用 户的就只有开关机键、通过键盘输入,显示器,USB插孔等,让用户和计算机进行交互,完成日 常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。
在上述定义的类中,日常工作里我们往往只希望别人使用函数而不修改数据,所以我们会在成员变量前加入访问限定符private,这也是一种封装。
5.类的作用域
类单独享有一个域,所以不同的类可以含有同名函数和变量
class stack
{
public:
void Init()
{
cout << "初始化栈" << endl;
}
void Push(int x)
{
cout << x << "被压栈了" << endl;
}
void Destroy()
{
cout << "销毁栈" << endl;
}
int stackSize()
{
return size;
}
private:
int* a;
int size;
int capacity;
};
class List
{
public:
void Init()
{
cout << "初始化顺序表" << endl;
}
void Push(int x)
{
cout << x << "顺序表尾插" << endl;
}
void Destroy()
{
cout << "销毁顺序表" << endl;
}
private:
int* a;
int size;
};
以上代码中,定义了一个顺序表和一个栈,两者都有Init,Destroy,Push,a,size。但是两者并不冲突,因为两者的类域不同。
6.类的实例化
类是对对象进行描述的,是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它。
可以理解为,类就是一个建筑图纸,其规定了一个建筑有哪些房间,房间大小,尺寸等等。但是图纸是不可以住人的,也就是说还不是一个可以使用的屋子,此时就需要实例化,根据图纸把屋子建出来。
接下来我们检测一个对象的大小:
class stack
{
public:
void Init()
{
cout << "初始化栈" << endl;
}
void Push(int x)
{
cout << x << "被压栈了" << endl;
}
void Destroy()
{
cout << "销毁栈" << endl;
}
int stackSize()
{
return size;
}
private:
int* a;
int size;
int capacity;
};
int main()
{
cout << sizeof(stack) << endl;//输出16
return 0;
}
输出16即16个字节是a,size,capacity三个成员的大小而不包括函数,这是因为类的成员变量放在对象本身的空间中,而函数会被放在一个公共代码段,因为函数是一致的。
7.this指针
class myclass
{
public:
void Init(int x, int y, int z)
{
_x = x;
_y = y;
_z = z;
}
void Add()
{
cout << _x + _y + _z << endl;
}
private:
int _x;
int _y;
int _z;
};
int main()
{
myclass c1;
myclass c2;
c1.Init(1, 2, 3);
c2.Init(4, 5, 6);
c1.Add();
c2.Add();
return 0;
}
调用Add函数时,Add函数并无形参,它是如何知道要完成1+2+3还是4+5+6?
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏 的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量” 的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编 译器自动完成。
this指针有下面几条特性
1. this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
2. 只能在“成员函数”的内部使用。
3. this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储this指针。
4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递。
比如我们可以在类中添加一个函数:
void tellMeThis()
{
cout << this << endl;
}
这样就可以在成员函数内部使用this指针了。
void changeThis()
{
this = NULL;
}
想要像这样修改this指针是非法的,因为this指针被const修饰,无法修改。