C++兼容C struct的用法
C++同时对struct进行了升级,把struct 升级成了类
// struct 不加访问限定符,默认是public
// class 不加访问限定符,默认是private
class Student
{
public://公有限定符号
// 类体:由成员函数和成员变量组成
void Init(const char* name, const char* gender, int age)
{
strcpy(_name, name);
strcpy(_gender, gender);
_age = age;
}
void Print()
{
cout << _name << " " << _gender << " " << _age << endl;
}
// 这里并不是必须加_
// 习惯加这个,用来标识成员变量
private://私有限定符号
char _name[20];
char _gender[3];
protected://保护限定符号
int _age;
};
// 封装:更严格管理设计
// 1、数据和方法封装到一起,类里面
// 2、想给你自由访问的设计成共有,不想给你直接访问的设计成私有
// 一般情况设计类,成员数据都是私有或者保护
//想给访问的函数是共有,不想给你访问时私有或保护
class Stack
{
private://私有限定符
void Checkcapaicty()
{}
public://公有限定符
void Init()//用户可以调用的函数接口
{}
void Push(int x)
{}
int Top()
{}
private://私有限定符保护对象
int* _a;
int _top;
int _capacity;
};
//两个类域
//class Stack和class Queue中的push函数是可以同时存在的
//他们不构成函数重载的原因是,函数重载在同一个作用域中才会发生
//此时两个push函数是在Stack和Queue两个不同的类的类域中
class Stack
{
public:
void Push(int x)
{}
};
class Queue
{
public:
void Push(int x)
{}
};
1、在类中定义函数,这样定义的函数自动被视为内联函数
class Stack
{
public:
// 在类里面定义
// 在类里面定义的函数默认是inline
void Init()
{
_a = nullptr;
_top = 0;
_capacity = 0;
}
// 在类里面声明
void Push(int x);
void Pop();
// 总结一下:实际中,一般情况下,短小函数可以直接在类里面定义
//长一点函数声明和定义分离
//private:
//这个地方是变量的声明,判断变量声明还是定义,就看他有没有开辟空间
int* _a;
int _top;
int _capacity;
};
2、在类中声明函数,在其他地方实现这种方式需要使用 : : 作用域解析符
void Stack::Push(int x)//作用域解析符
//把push从Stack的类域中放出来
{
// ...
_top++;
}
// 类中既有成员变量,又有成员函数
//A1类的大小为8个字节,int类型成员变量和char类型成员变量内存对齐
class A1 {
public:
void f1(){}
private:
int _a;
char _ch;
};
// 总结:没有成员变量的类对象,编译会给他们分配1byte占位
//表示对象存在过
// 类中仅有成员函数(类的大小为1个字节)
class A2 {
public:
void f2() {}
};
// 类中什么都没有---空类(类的大小为1个字节)
class A3
{
//char _ch;
};
class Date
{
public:
/*void Print(Date* const this)//这是编译器编译后修改的版本
//在形参位置设置了一个隐含的this指针
{
cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}*/
void Print()
{
cout << this << endl;
cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}
这是编译器编译后修改的版本
/*void Init(Date* const this, int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}*/
void Init(int year, int month, int day)
{
//this = nullptr; this指针本身不能修改,因为他是const修饰的
// this指向对象可以被修改
cout << this << endl;
this->_year = year;
_month = month;
_day = day;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
class Date
{
public:
Date()//构造函数无参数版本(如果我们不写,编译器会自动生成)
{
_year = 1;
_month = 1;
_day = 1;
}
Date(int year, int month, int day)//构造函数有参数版本
{
_year = year;
_month = month;
_day = day;
}
Date(int year = 1, int month = 1, int day = 1)//构造函数的全缺省写法
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year; // 年
int _month; // 月
int _day; // 日
};
int main()
{
//Date d1(); // 不能这么写
Date d1;//构造函数的调用
d1.Print();
Date d2(2022, 5, 15);//可以全传参数调用
d2.Print();
Date d3(2022);//也可以只传一两个参数缺省调用
d3.Print();
Date d4(2022, 10);//这也是缺省调用
//构造函数的名字和类名相同
d4.Print();
return 0;
}
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。(意思是如果我们不写构造函数,编译器会默认生成一个无参构造函数)
- 重点:C++将类型分为两种
1、内置类型/基本类型:int/char/double/指针…
2、自定义类型:class/struct去定义类型对象
(默认生成构造函数对于内置类型成员变量不做处理,对于自定义类型成员变量才会处理,调用这个自定义类型成员的默认构造函数)
3、总结:如果一个类中的成员变量全是自定义类型,并且这些自定义类型成员都提供了默认构造函数,我们就可以不用写构造函数,让编译器默认生成的无参构造函数。如果还有内置类型的成员,需要在声明时给缺省值(所以大多数情况下我们都自己写构造函数,不用默认生成的)
在C++定义中,无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。(总而言之就是不用传参就能调用的构造函数,都被定义为默认构造函数)
class Stack
{
public:
// Init(构造函数类似于我们之前写的初始化函数)
// Destroy(析构函数类似于之前学习的销毁函数)
Stack(int capacity = 10)//全缺省构造函数(默认构造函数的一种)
{
_a = (int*)malloc(sizeof(int)*capacity);
assert(_a);
_top = 0;
_capacity = capacity;
}
~Stack()//默认析构函数
{
cout << "~Stack():" << this << endl;
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
// 我们不写编译器会默认生成一个拷贝构造
// 1、内置类型的成员会完成值拷贝,浅拷贝。
//浅拷贝的问题:如果成员变量有开辟空间(例如数据结构栈)
//那么浅拷贝会导致拷贝前后的两个变量指向同一块空间
//(修改数据会互相影响,而且同一块空间在析构时会析构两次,程序会崩溃)
// 2、自定义类型的成员,去调用这个成员的拷贝构造
//结论:一般的类,自己生成的拷贝构造就够用了
//只有像栈这样的类,自己直接管理资源,自己写一个拷贝构造(需要实现深拷贝)
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
//运算符重载 -- 函数
//函数名:operator 运算符
//参数:运算符的操作数
//返回值:运算符运算后结果
class Date
{
public:
// 默认生成的析构函数,内置类型成员不做处理,自定义类型成员会去调用它的析构函数
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
//运算符重载函数一般都直接写在类里
bool operator==(const Date& d)//运算符重载函数
// bool operator==(Data* const this,Data d)
// “==”号是双目操作符却只有一个参数的原因就是类里面会自动处理生成一个this指针
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
// 2022 5 16
// 2021 10 16
bool operator<(const Date& d)//运算符重载函数
//利用引用操作符,就可以不调用拷贝构造
//用const修饰引用,让引用不改变(防止出现d._year == year 的错误,这样写会报错)
{
if ((_year < d._year)
|| (_year == d._year && _month < d._month)
|| (_year == d._year && _month == d._month && d._day < d._day))
{
return true;
}
else
{
return false;
}
}
private:
int _year;
int _month;
int _day;
};
//运算符重载函数调用方法
if (d1.operator==(d2))
{
cout << "==" << endl;
}
if (d1 == d2) // 编译器会处理成对应重载运算符调用 if (operator==(d1, d2))
{
cout << "==" << endl;
}
class Date
{
public:
//d2 = d1; -> d2.operator=(&d2, d1)
// d1 = d1
Date& operator=(const Date& d)//这里使用传引用返回,如果传值返回还需要拷贝
//自定义类型拷贝需要调用拷贝构造,代价较大,所以使用传引用返回
{
if (this != &d)//判断是不是自己给自己赋值
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;//解引用this指针
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2022, 5, 16);
Date d2(2022, 5, 18);
Date d3(d1); // 拷贝构造 -- 一个存在的对象去初始化另一个要创建的对象
d3 = d2 = d1; // 赋值重载/复制拷贝 -- 两个已经存在对象之间赋值
(d3 = d2) = d1; // 赋值重载/复制拷贝 -- 两个已经存在对象之间赋值
d1 = d1;//自己给自己赋值的例子
int i = 0, j, k;
k = j = i;//赋值操作符是有返回值的
//(k = j) = i;
return 0;
}
//用日期类型做个总结
class Date
{
public:
int GetMonthDay(int year, int month)//判断某年某月有多少天的函数
{
int monthDayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2 && isLeapYear(year))
{
return 29;
}
else
{
return monthDayArray[month];
}
}
// 默认生成的析构函数,内置类型成员不做处理,自定义类型成员会去调用它的析构函数
Date(int year = 1, int month = 1, int day = 1)
{
if (year >= 1 &&
month <= 12 && month >= 1 &&
day >= 1 && day <= GetMonthDay(year, month))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "日期非法" << endl;
}
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
// d1 + 100; -> d1.operator+(int day)
// d1 - 100;
// d1 - d2;
Date operator+(int day);
Date operator-(int day);
int operator-(const Date& d);
//bool operator>(const Date& d)
//bool operator>=(const Date& d)
//bool operator==(const Date& d)
//bool operator!=(const Date& d)
//bool operator<=(const Date& d)
bool operator<(const Date& d)
{
if ((_year < d._year)
|| (_year == d._year && _month < d._month)
|| (_year == d._year && _month == d._month && d._day < d._day))
{
return true;
}
else
{
return false;
}
}
private:
int _year;
int _month;
int _day;
};