C++学习笔记——类和对象
//先语:在前面对C++有了一个初步的认识,下面便是与C不同的一点。类和对象。在C++里引入了面向对象的编程思想。便有了类和对象。
1.类与对象的初步认知
★概念
类是现实事务的抽象,如猪,鸡,鸭子,为家禽类。
对象是类的实例化,如老虎是猫科类的实例
★类的引入
在C语言中,结构体不能定义函数,而c++可以。c++里struct用class代替
类的定义 :平时应该在意一下命名规则,形成自己的编程风格。前面有所介绍:可以点击进入传送门
class MyClass
{//成员变量和成员函数
public:
void set_data(int _obj1, int _obj2);
void My_Class(int _a);
private:
int obj1_;
char obj2_;
};//注意有分号
class为定义类的关键字,MyClass为类的名字,{}中为类的主体,注意类定义结束时后面分号
一般声明放在.h文件中,类的定义放在.cpp文件中,结合后面的类的作用域
★类的访问限定符及封装
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用
【访问限定符说明】
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. class的默认访问权限为private,struct为public(因为struct要兼容C)
★类的作用域
类的所有成员都声明或定义在类的作用域类,当作类外定义成员,需要用::作用域限定符
class MyClass
{
public:
void set_data(int _obj1, int _obj2);//声明
void My_Class(int _a);
private:
int obj1_;
char obj2_;
};
#include"class_object.h"
void MyClass::set_data(int _obj1, int _obj2)//定义
{
obj1_ = _obj1;
this->obj2_ = _obj1;//this彩蛋
}
void MyClass::My_Class(int _a)
{
cout << _a << endl;
}
★类的实例化
创建一个类,不占实际物理空间,空类的话会占一个字节。但实例化出的对象就要占空间了。比如:家具类,一个类并没有分配空间,但是实例出床,写字台,就会占我们的物理空间。
创建出的实例,我们会通过构造函数来对它进行初始化。
int main()
{
MyClass test;//这个调用的四MyClass()这个默认构造函数
system("pause");
return 0;
}
★.类的对象模型
·类对象的大小
一个类的大小,实际就是该类中成员大小之和,当然也要进行内存对齐,内存对齐和C语言的结构体对齐是一个道理。这里都有相关介绍:可以点击进入传送门
注意,空类比较特殊,编译器给了空类一个字节来唯一标识这个类
★类成员函数的this指针
1. this指针的类型:类类型* const
2. 只能在“成员函数”的内部使用
3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递
void MyClass::set_data(int _obj1, int _obj2)
{
obj1_ = _obj1;
this->obj2_ = _obj1;//就算没有this,编译器会自动完成
}
-
2、类的六个默认成员函数
★构造函数
构造函数:一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次
任务:
初始化对象
特点:
1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器自动调用对应的构造函数。
4. 构造函数可以重载
5.若用户定义了构造函数,编译器便不再自动生成。
6.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)//全缺省
{
year_ = year;
month_ = month;
day_ = day;
}
Date()//无参的构造函数
{}
private:
int year_;
int month_;
int day_;
//const int& ra;
};
//上面两个函数只能出现一个,并且成为默认构造函数,同时出现,Date test2会产生二义性,
//编译器不知道用哪个构造函数来初始化。当然编译器也会提醒你
7.如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
★析构函数
析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作
class String
{
public:
String(const char* str = "moren")//全缺省的构造函数
{
str_ = (char*)malloc(strlen(str) + 1);
strcpy(str_, str);
}
//析构函数
~String()
{
cout << "正在清空资源" << endl;
free(str_);
}
void display()
{
cout << str_ << endl;
}
private:
char* str_;
};
特点:
1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值。
3. 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
★拷贝构造函数
构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
//拷贝构造
Date(const Date& date)//引用传参
{
year_ = date.year_;
month_ = date.month_;
day_ = date.day_;
}
特点:
1. 拷贝构造函数是构造函数的一个重载形式。
2. 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用(这里可能会有疑问)
3.若未显示定义,系统生成默认的拷贝构造函数
·浅拷贝:只拷贝对象本身内容
·深拷贝:拷贝对象本身内容和对象的资源
编译器生成的是浅拷贝
★运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
bool operator == (Date& d2)
{
return year_ == d2.year_&&month_ == d2.month_&&day_ == d2.day_;
}
//赋值运算符
Date& operator = (const Date& d)
{
if (this != &d)
{
year_ = d.year_;
month_ = d.month_;
day_ = d.day_;
}
return *this;
}
注意:
✧不能通过连接其他符号来创建新的操作符:比如[email protected]
✧重载操作符必须有一个类类型或者枚举类型的操作数
✧用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不能改变其含义
✧作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的,操作符有一个默认的形参this,限定为第一个形参
✧* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。
赋值运算符:只能给已经存在的对象进行复制,改变它的内容,若没有存在则会调拷贝构造函数
1. 参数类型
2. 返回值
3. 检测是否自己给自己赋值
4. 返回*this
★const成员
✧const修饰类的成员函数(C语言曾学过)
将const修饰的类成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改
//const 修饰成员函数
void display2() const
{
cout << year_ << "/" << month_ << "/" << day_ << endl;
}
private:
int year_;
int month_;
int day_;
//const修饰成员变量
const int ra;
问题:
1. const对象可以调用非const成员函数吗?
2. 非const对象可以调用const成员函数吗?
3. const成员函数内可以调用其它的非const成员函数吗?
4. 非const成员函数内可以调用其它的const成员函数吗?
const对象不能调非const成员函数,但非const对象能调是const修饰的成员函数。
就好比const修饰的是胖子,瘦子是非const,瘦子能穿胖子的衣服,但胖子穿不了瘦子的衣服。
-
3、初始化列表
✧初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。和赋值顺序相同,和初始化顺序无关
class Date
{
public:
Date(int year, int month, int day)
: year_(year)
, month_(month)
, day_(day)//这里没有分号
{
……
}
private:
int year_;
int month_;
int day_;
};
特点:
——每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
——类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量
const成员变量
类类型成员(该类没有默认构造函数)
class DiffA
{
public:
Diff1(int a)//编译器不再生成构造函数
:test_(a)
{
……
}
private:
int test_;
};
class DiffB
{
public:
DiffB(int a, int ref)
:_aobj(a)
,_ref(ref)
,_n(10)
{
……
}
private:
Diff1 aobj_; //没有无参构造,也没有全缺省构造。所以没有默认构造函数;
int& ref_; // 引用
const int n_; // const
};
——尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使
用初始化列表初始化。
——成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
-
3、运算符重载:完善class Date类
头文件
#pragma once
#include<iostream>
using namespace std;
class Date
{
public:
void display()
{
cout << year_ << "/" << month_ << "/" << day_ << endl;
}
void judge()
{
cout << "您输入的年份非法,已恢复至默认年份1900/1/1" << endl;
year_ = 1900;
month_ = 1;
day_ = 1;
}
Date(int year, int month, int day);
Date(const Date& d);
Date& operator=(const Date& d);
int getday(int year, int month)
{
static int _day;
int _mon[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
{
_mon[1] = 29;
}
_day = _mon[month - 1];
return _day;
}
Date operator+(int days);
Date operator-(int days);
int operator-(const Date& d);
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
bool operator>(const Date& d)const;
bool operator>=(const Date& d)const;
bool operator<(const Date& d)const;
bool operator<=(const Date& d)const;
bool operator==(const Date& d)const;
bool operator!=(const Date& d)const;
private:
int year_;
int month_;
int day_;
};
源文件
#include"ClassDate.h"
#include<iostream>
using namespace std;;
Date::Date(int year = 1900, int month = 1, int day = 1)
{
if (year<=0 && month <=0 && month>12)
{
judge();
}
else
{
if(month == 2 )
{
if (!(year % 4 == 0 && year % 100 != 0 || year % 400 == 0) && day > 28)
{
judge();
}
else if ((year % 4 == 0 && year % 100 != 0 || year % 400 == 0) && day > 29)
{
judge();
}
year_ = year;
month_ = month;
day_ = day;
}
}
}
//拷贝构造
Date::Date(const Date& d)
{
year_ = d.year_;
month_ = d.month_;
day_ = d.day_;
}
//重载 赋值=
Date& Date::operator=(const Date& d)
{
year_ = d.year_;
month_ = d.month_;
day_ = d.day_;
return *this;
}
//重载 加 + int
Date Date::operator+(int days)
{
//Date temp;
this->day_ += days;
if (this->day_ >this->getday(this->year_,this->month_))
{
while (this->day_ > this->getday(this->year_, this->month_))
{
this->day_ -= this->getday(this->year_, this->month_);
if (this->month_ == 12)
{
++this->year_;
this->month_ =1;
}
else
{
++this->month_;
}
}
}
return *this;
}
//重载 减 - int
Date Date::operator-(int days)
{
this->day_ -= days;
if (this->day_ < 0)
{
while (this->day_ < this->getday(this->year_, this->month_ -1))
{
this->day_ += this->getday(this->year_, this->month_ -1 );
if (this->month_ == 12)
{
--this->year_;
this->month_ = 12;
}
else
{
--this->month_;
}
}
}
return *this;
}
//重载 前自增
Date& Date::operator++()
{
(*this) + 1;
return *this;
}
//重载 后自增
Date Date::operator++(int)
{
(*this)++;
return *this;
}
//重载 >
bool Date::operator>(const Date& d)const
{
return this->year_ > d.year_ ||
(this->year_ == d.year_ && this->month_ > d.month_) ||
(this->year_ == d.year_ && this->month_ == d.month_ && this->day_ > d.day_);
}
bool Date::operator>=(const Date& d)const
{
return (*this > d) ||
(this->year_ == d.year_ && this->month_ == d.month_ && this->day_ == d.day_);
}
bool Date::operator<(const Date& d)const
{
return !(*this >= d);
}
bool Date::operator<=(const Date& d)const
{
return !(*this > d);
}
bool Date::operator==(const Date& d)const
{
return (this->year_ == d.year_ && this->month_ == d.month_ && this->day_ == d.day_);
}
bool Date::operator!=(const Date& d)const
{
return !(*this == d);
}
//重载 减 对象
int Date::operator-(const Date& d)
{
int val = 0;
Date _min(*this);
Date _max(d);
int flag;
if (*this > d)
{
_min = d;
_max = *this;
flag = -1;
}
while (_min != _max)
{
_min + 1;
++val;
}
return val;
}
//重载 自减
Date& Date::operator--()
{
(*this)--;
return *this;
}
Date Date::operator--(int)
{
(*this) - 1;
return *this;
}
写在后面:转载请附上链接哦,原创辛苦。欢迎指出错误。