C++(类和对象)
C++中的类
C++中兼容对C语言中struct的所有用法.同时C++对struct进行了语法的升级.将struct升级成了类.
// c++中对于struct的改进:
struct Stack
{
int* a;
int top;
int capacity;
}
int main()
{
Stack s;// 这里可以直接使用Stack进行使用,而不再需要struct关键字了
return 0;
}
注意:
在c++中,类名就是类型,Stack就是类型,不需要加上struct关键字了
类里面可以定义函数.例如
// c++中对于struct的改进: struct Stack { int* a; int top; int capacity; //可以直接在类中定义函数. void Init() { a = NULL; top = 0; capacity = 0; } void Push(int x) { ..... } } int main() { Stack s;// 这里可以直接使用Stack进行使用,而不再需要struct关键字了 s.Init(); // 直接这样调用函数. return 0; }
Class的用法
访问限定符:
- 使用public修饰的符号,在类外可以被访问
- 使用protected和private修饰的成员在类外面不可以被访问.
- 访问限定符的作用域 从当前位置开始,直到下一个访问限定符出现为止.如果没有出现下一个访问限定符,那么直接就修饰到结尾.
- class的访问限定修饰符默认是private,struct默认的是public(为了和C语言适配)
虽然Class中的默认访问限定修饰符的修饰方式是private,但是最好还是将private的成员加上显性的private修饰符.
声明与定义分离
//stack.h文件
#include <iostream>
using namespace std;
class Stack
{
private:
int *_a;
int _top;
int _capacity;
public:
void Push(int x);
void Init();
// 注意:默认在类中定义的函数就是内联函数,前面自动会有一个inline.
// 但是,如果函数体太长,编译器会忽略inline,将其作为普通函数处理.
//正确的做法:
// 长的函数声明与定义分离
// 短的函数直接在类中定义即可.
bool Empty()
{
return top == 0;
}
};
// stack.cpp文件
#include"stack.h"
// 声明与定义分离的时候,需要使用Stack::进行修饰
void Stack::Init()
{
_a = 0;
_top = 0;
_capacity = 0;
}
void Stack::Push(int x)
{
//....
}
// main.cpp文件
#include"stack.h"
int main()
{
return 0;
}
小技巧:
建议在私有的成员之前或者后面加上_来区分私有成员.
类的作用域
C++中花括号定义的都是域.
类的实例化
注意:在类中的成员:诸如:
class Stack
{
private:
// 这里的成员都是声明
int *_a;
int _top;
int _capacity;
public:
void Push(int x);
void Init();
// 注意:默认在类中定义的函数就是内联函数,前面自动会有一个inline.
// 但是,如果函数体太长,编译器会忽略inline,将其作为普通函数处理.
//正确的做法:
// 长的函数声明与定义分离
// 短的函数直接在类中定义即可.
bool Empty()
{
return top == 0;
}
};
// main.cpp
int main()
{
// 此处才是定义对象
Date d1;
d1.Empty();
return 0;
}
类和对象的关系:类就是设计图纸,对象就是建造出来的房子.
注意:同一个类的不同对象中的函数是一样的,但是不同对象中的成员变量是不一样的,函数会被存储到公共的区域中,一旦需要调用函数就直接去这个公共的区域中寻找即可.
类的成员变量的存储还是按照结构体内存对齐法进行对齐的.
注意:就算一个类没有任何成员,这个类的大小是1.(无成员变量的类大小是1字节)
#include"stack.h"
class B{};
int main()
{
cout<<sizeof(B)<<endl;
return 0;
}
无成员变量的类的大小是1字节,这个字节不存储任何数据,只是标识这个类存在过.
#include"stack.h"
class C{
public:
void f(){}
};
int main()
{
cout<<sizeof(C)<<endl;
return 0;
}
this指针
类中的成员函数的参数默认第一个是this,但是没有显示的写出来,并且在成员函数中访问成员变量时,前面也会有一个默认的this,例如: this->a.但是this指针不能显式的写实参和形参,可以在类中显式的使用.
#include<iostream>
#include"date.h"
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
void Init(int year,int month,int day)
{
this->_year=year;
_month=month;
_day=day;
}
void Print()
{
cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
cout<<this->_year<<"-"<<this->_month<<"-"<<this->_day<<endl;
}
};
int main()
{
Date d1;
d1.Init(2022,10,1);
d1.Print();
return 0;
}
那么this指针是存储在哪里的?答:在vs中,this指针是存储在栈帧中的.也就是存储在寄存器中的.
// 这段代码:会正常执行.
#include<iostream>
using namespace std;
class A{
private:
int a;
public:
void Print(){
cout<<"A::Print()"<<endl;
}
};
int main()
{
A* a = nullptr;
a->Print();
return 0;
}
// 但是下面这样子就会运行报错.
#include<iostream>
using namespace std;
class A{
private:
int _a;
public:
void Print(){
cout<<_a<<endl;
}
};
int main()
{
A* a = nullptr;
a->Print();
return 0;
}
默认的6个成员函数
构造函数
构造函数的主要任务并不是开空间,而是初始化对象.
特点:
- 函数名和类名相同
- 无返回值
- 对象实例化时自动调用
- 构造函数可以重载
- 不写构造函数,编译器会默认生成默认构造函数.这个默认的构造函数:对于内置类型不做处理,对于自定义类型,回去调用它的默认构造函数.(如果没有默认构造函数,就会调用初始化列表)C++11特意对此打了补丁,支持声明时给缺省值.
- 无参的构造函数和全缺省的构造函数都称为默认构造函数.并且默认构造函数只能有一个.注意:无参数构造函数,全缺省构造函数.我们没写编译器默认生成的构造函数,都可以认为是默认构造函数.但是:这三个函数不能同时存在,否则会有调用歧义.
#include<iostream>
#include"date.h"
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
// 构造函数
Date(int year,int month,int day)
{
_year=year;
_month=month;
_day=day;
}
// 无参数构造
Date()
{
_year=1;
_month=1;
_day=1;
}
void Print()
{
cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
}
};
int main()
{
// 默认调用无参数构造,注意这里不能加括号
Date d1;
d1.Print();
// 主动调用有参数构造
Date d2(2022,10,1);
d2.Print();
return 0;
}
注意:如果没有主动写构造函数的话,编译器会自动生成一个构造函数,但是默认生成的就是随机值.例如
#include<iostream>
#include"date.h"
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
void Print()
{
cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
}
};
int main()
{
Date d1;
// 打印随机值
d1.Print();
return 0;
}
默认生成的构造函数:
对于自定义类型(class ,struct等),会去调用它的构造函数.
对于内置类型(int/double/指针)的成员不做处理.(例如:Date* 也是内置类型)
但是我们可以在声明的时候给成员变量赋一个缺省值.
#include<iostream>
#include"date.h"
using namespace std;
class Date
{
private:
// C++11支持
int _year = 1;
int _month = 1;
int _day = 1;
public:
void Print()
{
cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
}
};
int main()
{
Date d1;
d1.Print();
return 0;
}
析构函数
功能:对象在销毁时,会自动调用析构函数,完成对象中的资源清理工作.
特点:
- 函数名是在类名之前加上~
- 无参数返回值类型
- 一个对象只能有一个析构函数.若没有显式定义,系统会自动生成默认的析构函数. 注意:析构函数不能重载
- 对象声明周期结束时,C++编译系统会自动调用析构函数.
默认生成析构函数的行为与默认生成构造函数相似:
- 内置类型成员不做处理
- 自定义类型成员会去调用它的析构函数.