系列文章目录
提示:C++类和对象的知识点,后续再补充
文章目录
- 系列文章目录
- 1.1C和C++中的结构体struct
- 1.2 类声明和变量分离
- 1.3 类的访问限定符和封装
- 1.4 类外使用成员函数 在类外使用::作用域解析符来指明成员属于哪个类
- 1.5 声明和定义的拓展
- 1.6 对象中只会存储成员变量,而不会存储成员函数。why?
- 1.7 类对象中隐含的this指针
- 1.8 构造函数:初始化对象
- 1.9 无参的构建函数和全缺省构造函数
- 1.10 析构函数
- 1.11 拷贝构造 :采用同类对象来构造新的对象
- 1.12 运算符重载
- 1.13
- 1.14
- 1.15
- 1.16
1.1C和C++中的结构体struct
C中,结构体只能定义变量
C++中,结构体可以定义变量和函数,C++中的结构体应该叫类。
struct Student
{
void SteStudentInfo(const char* name,const char* gender,int age)
{
strcpy(_name,name);
strcpy(_gender,gender);
_age = age;
}
void printStudentInfo()
{
cout<<_name<<" "<<_gender<<" "<<_age<<endl;
}
char _name[20];
char _gender[3];
int _age;
};
但结构体它的默认为权限公开,所有对象都可以访问 结构体定义的变量,没有默认私有,就是封装特性。所有C++专门定义一个class类来实现面向对象编程
1.2 类声明和变量分离
person.h 文件中
class Person
{
public:
void ShowInfo();
private:
char* _name;
char* _sex;
int _age;
};
person.cpp文件中
#inclde "person.h"
void Person::showInfo()
{
cout<<_name<<"_"<<_sex<<"_"<<_age<<_endl;
}
1.3 类的访问限定符和封装
public(公有) 类外也可以访问
protected(保护)和private(私有)类外无法访问
class默认访问权限:私有 private
struct默认访问权限:共有 public 兼容C
1.4 类外使用成员函数 在类外使用::作用域解析符来指明成员属于哪个类
class Person
{
public:
void ShowInfo();
private:
char* _name;
char* _sex;
int _age;
};
void Person::showInfo() //成员函数类外需要指明所属类
{
cout<<_name<<"_"<<_sex<<"_"<<_age<<_endl;
}
1.5 声明和定义的拓展
声明是一种承诺,其不分配空间;
定义是对象的实例化,分配空间;
声明可以多次,但是定义只能一次
声明可以在多个源文件中,通过#ifndef来解决,而定义只能在一个源文件中。
1.定义的情况
万物皆可以对象
类名 对象名;(数据类型 变量) +函数定义
```cpp
int x; //变量定义 分配空间
Person p; //对象定义 分配空间
int Add(int x,int y) //函数定义 分配栈空间
{
return x+y;
}
1.声明的情况
extern int x;
(extern) int Add(intx,int y);
1.6 对象中只会存储成员变量,而不会存储成员函数。why?
一个类可以实例化出多个对象,这些对象的成员函数都一样,变量值不一样,为了节约空间,会把成员变量存储在对象中,成员函数存储在公共代码区。
注意:如果一个空对象有一个字节的空间,why,占位,方便取值。
1.7 类对象中隐含的this指针
1.this指针指向 谁调用指向谁, this指针是类中的成员函数的第一个参数
2.this指针不能为空,空的this,会导致 this->变量是报错
class Date
{
public:
void Init(int year,int month,int day)
{
_year = year;
_month =month;
_day = day;
}
>>>>>>>>>>>>>>>>编译器编译为>>>>>>>>>>>>>>>>>>
void Init(Data* this,int year,int month,int day)
{
this->_year = year;
this->_month =month;
this->_day = day;
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
private:
int _year;
int _month;
int _day;
};
int mian()
{
Date d1;
d1.Init(2020,4,7)//
>>>>>>>>>>>>>>>>编译器编译为>>>>>>>>>>>>>>>>>>
d1.Init(&d1,2020,4,7);
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
}
1.8 构造函数:初始化对象
1.函数名和类名一样
2.无返回值
3.对象实例化时自动调用
4.构造函数可以重载
5.如果未定义构造函数,编译器会自动生成一个无参的默认构造函数
6.默认的析构在定义对象 会随机初始化内置类型的值
对于自定义类型 会调用对于的构造函数
class Time
{
public:
Time() //构造函数 函数名和类名一样 无返回值
{
_hour = 0;
_minute = 0;
_second =0;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
void Print()
{
cout<<_year<<"_"<<_month<<"_"<<_day<<endl;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
int main()
{
Date d1;
//定义对象d1时,
对象空间会创建变量int _year int _month int _day 这些对调用g++编译器生成的构造函数初始化为随机值
而创建自定义类型Time _t ,也相当于实例化Time类,会自动调用Time的构造函数,使得_t._hour = 0;_t._minute = 0;_t._second =0;
d1.Print();
//_year 随机值
//_month
return 0;
}
1.9 无参的构建函数和全缺省构造函数
为了兼容 Data d1;和Data d1(2020,4,6);这二种写法
可以采用函数重载来实现,但需要写二个函数
class Date
{
public:
Date(int year,int month,int day)//兼容Data d1(2020,4,6);
{
_year = year;
_month =month;
_day = day;
}
Date()//兼容Data d1;
{
_year = 0;
_month =0;
_day = 0;
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
采用全缺省构造函数可以合二为一
Date(int year=0,int month=0,int da=0)//兼容Data d1(2020,4,6);和//Data d1;
{
_year = year;
_month =month;
_day = day;
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
private:
int _year;
int _month;
int _day;
};
1.10 析构函数
对象什么周期到了,自动调用,用来清理和释放空间资源。
1.函数名为 ~类名
2.无参 无返回值
3.一个类只有一个析构函数
4.未定义,编译器会自动生成一个默认析构函数
5.自定义类型 会调用它自身的构建函数/析构函数
class Stack
{
~Stack()
if(_a)
{
free(_a);
_a = nullptr;
_size = _capacity = 0;
}
private:
int * _a;
int _size;
int _capacity;
};
1.11 拷贝构造 :采用同类对象来构造新的对象
以下是润色后的内容:
注意:使用传值的方式来实现拷贝构造函数会导致无限递归。在创建对象时,会调用一次拷贝构造函数。
因此,应该采用传引用的方式来实现拷贝构造函数。
疑问:如果传的是引用,那 d1
和 d2
不是同一块空间吗?
传递引用:d1
作为引用传递给拷贝构造函数,这只是为了访问 d1
的成员,而不是共享内存。
创建副本:拷贝构造函数会创建一个新的对象 d2
,并将 d1
的成员值复制到 d2
中。因此,d2
和 d1
是两个不同的对象,存在于不同的内存地址。
在创建对象时,会为 d2
分配一个新的对象空间(深拷贝),而传引用不会再次拷贝。
**总结:**使用传值的方式来实现拷贝构造函数,在创建对象时存在一个调用拷贝构造函数(产生新空间d2),在传参时还有创建一个临时对象tmp,这样起码会有二次深拷贝。
而采用采用传引用的方式来实现拷贝构造函数。只会在创建对象时存在一个调用拷贝构造函数(产生新空间d2),在传参时不在创建临时对象tmp,这样起码就不会有第二次深拷贝。
class Data
{
Data(Date d)//存在递归拷贝的问题
{
_year =d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
Data d2(d1);//或者Data d2=d1;二者等效
关于构造函数Data(Date d)//存在递归拷贝的问题分析:
1.对象初始化时会地调用构造函数,调用之前要传参即将d1传给Data(Date d)中的参数d,由于这个是通过传值的方式传参,会创建一个临时Date tmp=d,会再次调用构造函数,调用之前要传参即将tmp传给Data(Date d),往复调用,无限递归。
无法理解记住即可
1.12 运算符重载
作用:实现自定义类型采用运算符 ,例如:
class Data
{
Data(Date &d)//
{
_year =d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
Data d1;
Data d2;
d2-d1;//需要自己定义
d2 == d1;//会被编译器编译为 operator==(d2,d1);
d1 > d2;