类和对象(中篇)
类的6个默认成员函数
构造函数、拷贝构造函数、析构函数、赋值操作符重载、取地址操作符重载、const修饰的取地址操作符重载构造函数
特殊的成员函数,在创建对象时,由编译器自动来调用,并且在对象的生命周期内只调一次,来完成对象的构造以及初始化。
特性:
1.名字与类型相同 2.没有返回值类型 3.编译器来调(对象构造时,系统自动调用对应的构造函数) 4.如果没有显示定义,编译器合成一个(没有参数) 5.支持重载 6.无参构造函数和带全缺省的构造函数都被称为----->缺省构造函数,缺省构造函数只能存在一个
class Date
{
public :
// 1.无参构造函数
Date ()
{}
// 2.带参构造函数
Date (int year, int month , int day )
{
_year = year ;
_month = month ;
_day = day ;
}
// 3.缺省参数的构造函数
Date (int year = 2000, int month = 1, int day = 1)
{
_year = year ;
_month = month ;
_day = day ;
}
// 4.半缺省参数的构造函数(不常用)
Date (int year, int month = 1)
{
_year = year ;
_month = month ;
_day = 1;
}
private :
int _year ;
int _month ;
int _day ;
};
-
析构函数
特殊的成员函数,当一个对象的生命周期结束时,C++编译系统会自动调用一个成员函数,这个成员函数即析构函数。
特征:
1.析构函数名是在类名前加上字符~
2.无参数返回值
3.一个类有且只有一个析构函数,若未显示定义,系统会自动生成缺省的析构函数
注意:析构函数不是删除对象,而只是清理资源,即收回对象的空间y
- 拷贝构造函数
特殊的成员函数,创建对象时使用对象来进行初始化,这时所用的构造函数称为拷贝构造函数。
特性:
1.拷贝构造函数其实是一个构造函数的重载
2.拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用。
3.若未显示定义,系统会默认生成缺省的拷贝构造函数,缺省的拷贝函数会按照成员的声明顺序依次对类成员进行初始化。
4.单参的
// 类类型对象的引用 , 一般情况下, const Date&
// 特殊:
Date(const Date& d)
{
this->_year = d._year;
_month = d._month;
_day = d._day;
}
-
深浅拷贝
- 浅拷贝:如果一个类用于资源(堆,或者是其他系统的资源),当这个类发生复制时,资源也进行复制。
- 深拷贝:一个类存在资源,复制时资源没有进行复制。
class SeqList
{
public:
SeqList()
{
cout << "SeqList():" << this << endl;
_array = NULL;
_size = 0;
_capacity = 0;
}
SeqList(int n, DataType data)
{
cout << "SeqLiat(int,int,int):" << this << endl;
_array = (DataType*)malloc(n * sizeof(DataType));
for (int i = 0; i < n; i++)
{
_array[i] = data;
}
_size = n;
_capacity = n;
}
void PrintfSeqList()
{
for (int i = 0; i < _capacity; i++)
cout << _array[i] ;
cout << endl;
}
~SeqList()
{
cout << "~SeqList():" <<this->_array[1]<< this << endl;
if (_array != NULL)
{
free(_array);
_capacity = 0;
_size = 0;
}
}
private:
DataType * _array;
size_t _size;
size_t _capacity;
};
int main()
{
SeqList s1(10, 5);
SeqList s2;
s2 = s1;
s1.PrintfSeqList();
s2.PrintfSeqList();
SeqList s3(10, 6);
s3 = s1;
return 0;
}
* 运算符重载
- 在类里面定义,参数只有一个,因为它隐含着一个this指针
Data operator+(int day)
{
Data temp(*this);
temp._day += 1;
return temp;
}
- 在类外面定义参数要有两个,要把那个你要操作的对象加上
Data operator - (const Data& d,int day)
{
Data temp(d);
temp._day -= 1;
return temp;
}
赋值运算符
所以,要自己写喽。当有了下面这个赋值运算符的函数时,就可以。先是d2 = d1,调用赋值运算符的重载,然后再用那个返回值给d3赋值,调用的是拷贝构造函数。
Data& operator=(const Data& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
void Test ()
{
Date date1 ;
Date date2 = date1; // 调用拷贝构造函数
Date date3 ;
date3 = date1 ; // 调用赋值运算符的重载
}
要注意四点:
1.传参为什么传引用? —————- 不产生临时变量,提高效率
2.返回为什么是当前对象,而不是d ——– 和连续赋值的规则一样
3.返回值为什么不是void ———— 因为要能连续赋值
4.那个if语句是在判断什么 ———– 当自己给自己赋值时,就不用赋了,提高效率
4.为什么返回引用 ——- 提高效率
- 前置++(返回的是类类型的引用)
Data& operator++()//前置++
{
_day += 1;
return *this;
}
- 后置++(注意:返回的是类类型,因为我们创建了一个临时变量,不能返回栈空间的地址,还有operator的后面的参数必须是int型)
Date operator++(int)
{
Date temp(*this);
_day += 1;
return temp;
}
//赋值
Date& operator=(const Date& d)
{
if(this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
//小于
bool operator<(const Date& d)
{
if(_year < d._year ||
_year == d._year && _month < d._month ||
_year == d._year && d._month == d._month && _day < d._day)
{
return true;
}
return false;
}
//是否相等
bool operator==(const Date& d)
{
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
//是否不相等
bool operator!=(const Date& d)
{
return !(*this == d);
}
// 前置++
Date& operator++()
{
_day += 1;
return *this;
}
// 后置++
Date operator++(int)
{
Date temp(*this);
_day += 1;
return temp;
}
//取地址
Date* operator&()
{
return this;
}
-
类的const成员函数
在函数后面加const,const修饰this指针所指向的对象,也就是保证调用这个 const成员函数的对象在函数内不能被改变。
const修饰形参,一般和引用同时使用
const修饰返回值
const修饰类数据成员,必须在构造函数的初始化列表中初始化
const修饰类成员函数,实际修饰隐含的this,表示在类中不可以对类的任何成员进行修改
在const修饰的成员函数中要对类的某个数据成员进行修改,该数据 成员定义声明是必须加mutable关键字
- 思考以下几种场景
- const对象可以调用非const成员函数和const成员函数吗?
不可以调非const成员函数。 - 非const对象可以调用非const成员函数和const成员函数吗?
都可以调。 - const成员函数内可以调用其它的const成员函数和非const成员函数吗?
不可以调非const成员函数。 - 非const成员函数内可以调用其它的const成员函数和非const成员函数吗?
都可以调。
- const对象可以调用非const成员函数和const成员函数吗?
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
cout << "Date(int,int,int):" << this << endl;
_year = year;
_month = month;
_day = day;
}
void SetYear(int year)
{
_year = year;
}
int GetYear()
{
return _year;
}
void PrintDate()const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
void TestFunc1()
{}
void TestFunc2()const
{}
void TestFuncWithConst()const
{
TestFunc2();
TestFunc1();//不可以
}
void TestFuncWithoutConst()
{
TestFunc1();
TestFunc2();
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2018, 7, 29);
d1.PrintDate();
d1.TestFunc1();
d1.TestFunc2();
const Date d2;
d2.PrintDate();
d2.TestFunc1();//不可以
d2.TestFunc2();
}
类的取地址操作符重载 和 const修饰的取地址操作符重载
这两个默认成员函数一般不用重新定义,编译器默认会生成,
只有一种情况下,才会需要你自己重载这两个操作符,那就是你只想让别人获取你指定的内容。
Date* operator&()
{
return this;
}
const Date* operator&()const
{
return this;
}
若10个变量,只有两个需要改变,则在这个变量前面加上mutable。
推荐阅读
-
Java面向对象(1)面向对象的思想概述以及类的介绍,封装和构造方法
-
面向对象\类\对象\封装
-
Python学习---面向对象---类的定义、创建及对象方法的调用
-
python学习笔记之面向对象中的静态方法、类方法、属性方法总结
-
Python学习之面向对象(类的特殊方法)
-
PHP中类的继承和用法实例分析,php实例分析
-
PHP面向对象的进阶学习(抽像类、接口、final、类常量)_php技巧
-
JavaEE基础day02 1.定义Java中的变量 四类八种 2.变量定义和使用的注意事项 3.数据类型的转换、强制数据类型转换4.算数运算符、比较运算符、逻辑运算符、赋值运算符、三元运算符
-
PHP通过反射动态加载第三方类和获得类源码的实例
-
asp.net 时间类 一周的周一和周末的日期