>
>
默认的成员函数
隐含的this指针
#include<iostream> using namespace std; class Date{ private: int _year; int _month; int _day; public: void ShowDate() { cout << _year << "-" << _month << "-" << _day << endl; } }; int main() { Date d1; d1.ShowDate(); return 0; }
- 在void ShowDate()的时候,其实是创建的void ShowDate(Date * this)。
- 每个成员函数都有一个指针形参,它的名字是固定的,称为this指针,this指针是隐式的。但是构造函数没有这个隐含的this指针。
- 编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参this指针。
- 编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参this指针。
- this指针是成员函数隐含指针形参,是编译器自己处理的,我们不能在成员函数的形参中添加this指针的参数定义,也不能在调用时显示传递对象的地址给this指针。但是我们可以在成员函数中使用this指针,比如this->_name;,this是可以写也可以不写的。同时this也可以作为返回值。
默认的成员函数
①构造函数(初始化)
#include<iostream> using namespace std; class Date{ private: int _year; int _month; int _day; public: void ShowDate() { cout << _year << "-" << _month << "-" << _day << endl; } Date() { cout << "无参数" << endl; } Date(int year, int month, int day){ cout << "带参数" << endl; _year = year; _month = month; _day = day; } }; int main() { Date d1; d1.ShowDate(); Date d2(2018, 2, 2); d2.ShowDate(); Date d3(); return 0; }
以上代码的输出结果为:
调用构造函数时前面是 对象名 + . 。
在构造函数无参时,不可以调用Date d1();即无参时调用不加括号。
成员变量如果是内置类型,构造函数什么都不做,而如果包含自定义类型,例如:Time t1;,则构造函数会调用它的构造函数,对其进行初始化。
缺省的构造函数:即:无参数或者全缺省。(建议写全缺省的)
#include<iostream> using namespace std; class Date { private: int _year; int _month; int _day; public: void ShowDate() { cout << _year << "-" << _month << "-" << _day << endl; } Date(int year=2018,int month=1,int day=1) { cout << "全缺省" << endl; _year = year; _month = month; _day = day; } }; int main() { Date d1; d1.ShowDate(); Date d2(2018,2,2); d2.ShowDate(); system("pause"); return 0; }
#include<iostream> using namespace std; class Date { private: int _year; int _month; int _day; public: void ShowDate() { cout << _year << "-" << _month << "-" << _day << endl; } Date(int year, int month=1) { cout << "半缺省" << endl; _year = year; _month = month; _day = 1; } }; int main() { Date d1(2018,2); d1.ShowDate(); Date d2(2018); d2.ShowDate(); system("pause"); return 0; }
输出结果为:
②拷贝构造函数
创建对象时使用同类对象来进行初始化,这时所用的构造函数称为拷贝构造函数(Copy Constructor),拷贝构造函数是特殊的构造函 数。
特征:- 拷贝构造函数其实是一个构造函数的重载。
- 拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用。
- 若未显示定义,系统会默认缺省的拷贝构造函数。缺省的拷贝构造函数会,依次拷贝类成员进行初始化。
定义拷贝构造函数:
Date(const Date& d){//加const是防止被拷贝的对象被改变,不加引用会变为无穷递归 _year = d._year; _month = d._month; _day = d._day; } //调用时 Date d1; Date d2(d1); Date d3=d1;
如果我们将成员变量的访问属性设为private,在这里也是可以直接访问私有的成员变量,因为:
- 在类的成员函数中可以直接访问同类对象的私有/保护成员
- C++的访问限定符是以类为单位的,也就是说在这个单位内的成员可以互相访问。
不写拷贝构造函数会自动生成,也会起作用。
③析构函数(清理工作)
当一个对象的生命周期结束时,C++编译系统会自动调用一个成员函数,这个特殊的成员函数即析构函数(destructor)。
构造函数是特殊的成员函数,其特征如下:
- 析构函数在类名加上字符 ~。
- 析构函数无参数无返回值。
- 一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数。
- 对象生命周期结束时,C++编译系统系统自动调用析构函数。
注意析构函数体内并不是删除对象,而是做一些清理工作。
- 首先要判断是否需要清理,析构函数是自动调用的,如果成员变量有自定义类型,会调用该类型的析构函数。
- 运算符重载
运算符重载特征:
- operator+ 合法的运算符 构成函数名(重载<运算符的函数名:operator< )。
重载运算符以后,不能改变运算符的优先级/结合性/操作数个数。
- 五个不能被重载的运算符:.*、::、sizeof、?:、.
- ④赋值运算符重载
拷贝构造函数是创建的对象,使用一个已有对象来初始化这个准备创建的对象。
赋值运算符的重载是对一个已存在的对象进行拷贝赋值。
Date d1 ; Date d2 = d1; // 调用拷贝构造函数
Date d3 ; d3 = d1 ; // 调用赋值运算符的重载
日期类的赋值运算符重载:https://blog.youkuaiyun.com/Acoustic323/article/details/79717204
Date& operator=(const Date& d){
if(this!&d){
this->_year=d._year;
this->_month=d._month;
this->_day=d._day;
}
return *this;//*this会生成一个临时变量且会调用拷贝构造函数。
}
//可以用引用返回,也可以无返回值,void,但是会影响连续赋值
类的成员变量有两种初始化方式:
初始化列表。
构造函数体内进行赋值。
初始化列表以一个冒号开始,接着一个逗号分隔数据列表,每个数据成员都放在一个括号中进行初始化。尽量使用初始化列表进行初始 化,因为它更高效。不管你写不写初始化列表,都会走一遍初始化列表。
- 必须放在初始化列表里面的成员变量
常量成员变量。(常量创建时必须初始化)
- 引用类型成员变量。(引用创建时必须初始化)
- 没有缺省构造函数的类成员变量。
成员变量按声明顺序依次初始化,而非初始化列表出现的顺序。
⑤取地址操作符重载
Date* operator&(){
return this;
}
- ⑥const修饰的取地址操作符重载
const Date* operator&() const{
return this;
}
如果需要一个功能为:不能取地址,有两种实现方法:
- 将return this改为return NULL;
只声明取地址符的重载,不定义。(会有报错,直接警告你不可以取地址)
- 思考:
const对象可以调用非const成员函数和const成员函数吗?
- 不可以
- 非const对象可以调用非const成员函数和const成员函数吗?
- 可以
- const成员函数内可以调用其它的const成员函数非const成员函数吗?
- 不可以
非const成员函数内可以调用其它的const成员函数非const成员函数吗?
可以
宏的优缺点:
- 优点:
- 宏常量——增强可维护性;
- 宏函数——增强效率,因为没有压栈的过程。
- 缺点:
- 不方便调试/函数可以调试形成栈帧。
- 没有类型安全的检查;
- 可读性比较差,宏函数的可维护性比较差,容易出错。
#define Swap(a,b) {int temp=a;\ //宏要写在一行 可以使用换行符\
a=b;\
b=temp;}
为了防止多次调用Swap时会多次生成temp变量可以在调用时加花括号。
内联函数(inline)
可以调试,会形成栈帧,在releuse下会展开,debug不会展开。
- inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的的函数不适宜使用内联。
- inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉 内联。
- inline必须函数定义放在一起,才能成为内联函数,仅将inline放在声明前是不起不作用的。
- 定义在类内的成员函数默认定义为内联函数。
class Date { public : void Func () // 定义在类内部默认为内联函数 {} void Display(); private : int _year ; // 年 int _month ; // 月 int _day ; // 日 }; inline void Date::Display (){ // 成员函数定义为内联 cout<<"year:" <<_year<< endl; cout<<"month:" <<_month<< endl; cout<<"day:" <<_day<< endl; } inline void Test() // 全局函数定义为内联 {}
c++建议以 const/枚举/内联来替代宏
友元函数(friend)
在类外的函数想调用私有的成员变量,可以在类中定义成“友元函数”。
在C++中友元函数允许在类外访问该类中的任何成员,就象成员函数一样,友元函数用关键字friend说明。
- 友元函数不是类的成员函数。
- 友元函数可以通过对象访问所有成员,私有和保护成员也一样。
class A{ friend void Fun();//友元函数 friend class B;//友元类 } void Fun(){ } class B{ }
友元是单向的,若想相互访问,在两个类内部都要定义友元。
尽量少使用友元,会破坏封装。
但是为什么存在友元?例如<<的重载
- 输入输出运算符的重载的友元函数。
class Date { public :// 1:operator<<可以定义为成员函数吗? // 2:返回值定义为void可以吗?为什么? friend ostream & operator<< ( ostream& os , const Date& d ); friend istream & operator>> ( istream& is , Date& d); private : int _year ; // 年 int _month ; // 月 int _day ; // 日 }; ostream & operator<<( ostream& os , const Date& d) { os<<"year:" <<d. _year<<endl ; os<<"month:" <<d. _month<<endl ; os<<"day:" <<d. _day<<endl <<endl; return os ; } // cin.operator<<(cin, d1) istream & operator>> ( istream& is , Date& d) { cout<<" 请分别输入年月日: "<<endl ; is>>d ._year; is>>d ._month; is>>d ._day; return is ; } void Test () { Date d1 ; cin>>d1 ; cout<<d1 ; }
类的静态成员
- 类里面static修饰的成员,成为静态类成员。
- 类的静态成员是该类型的所有对象对象所共享。
可以统计总共创建了多少次对象:
class Date {
public :
Date () {
cout<<"Date ()" <<endl;
++ sCount; }
void Display () {
cout<<"year:" <<_year<< endl;
cout<<"month:" <<_month<< endl;
cout<<"day:" <<_day<< endl; }
// 静态成员函数
static void PrintCount() {
cout<<"Date count:" <<sCount<< endl;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
private :
static int sCount; // 静态成员变量,统计创建时间个数
};
// 定义并初始化静态成员变量
int Date::sCount = 0;
void Test () {
Date d1 ,d2;
// 访问静态成员
Date::PrintCount ();
}
没有this指针,不能调用Show,不能访问对象。
静态成员属于类,也属于类中所有的对象。
- Date d1;
- Date ();//临时对象,匿名对象,生命周期只在当前一行,用完后马上析构不占用空间
- fun(Date());
- N中构造拷贝构造的优化
Test1中调用了2次AA的拷贝构造函数,1次AA的赋值运算符函数的重载。
Test2中调用了2次AA的拷贝构造函数,0次AA的赋值运算符函数的重载。
Test3中调用了3次AA的拷贝构造函数,0次AA的赋值运算符函数的重载。
class AA{
};
AA f(AA a)
{
return a;
}
void Test1()
{
AA a1;
a1 = f(a1);
}
void Test2()
{
AA a1;
AA a2 = f(a1);
}
void Test3()
{
AA a1;
AA a2 = f(f(a1));
}