本博客将记录:类这个知识点的笔记!
(这个在学习C++基础课程时已经学习过一次了,这里再次简单地回顾一下而已)
这方面知识分为以下5个点:
一、构造函数
二、多个构造函数
三、函数的默认参数
四、隐式转换和explicit(显式类型转换)的用法
五、构造函数初始化列表
一、构造函数:
什么是构造函数呢?在类中,有一种特殊的成员函数,它的名字和类名相同,我们在创建类的对象时,系统会自动地为我们调用这个特殊的成员函数,这个函数就叫做:构造函数!
现在拿上一3.2小节的代码做例子:
//Time.h中定义了的构造函数 class Time{ public: Time():m_Hour(0), m_Minute(0), m_Second(0){} Time(int h,int m,int s):m_Hour(h), m_Minute(m), m_Second(s){} } //main.cpp int main(void){ Time t1;//定义了一个Time类型的对象 //此时编译器会为我们自动地调用Time():m_Hour(0), m_Minute(0), m_Second(0){}这个函数 Time t2(11,14,5);//定义了要给Time对象并赋初值为(11,14,5); //此时编译器会为我们自动地调用Time(int h,int m,int s):m_Hour(h), m_Minute(m), m_Second(s){} Time t3 = Time(11,14,5);//一般很少用下面这几种方式来创建对象,一般都用上面2种方式do Time t4 = Time{11,14,5}; Time t5 { 11,14,5 }; return 0; }
注意事项:
①构造函数没有返回值,这也是构造函数的独特之处(连void都不需要写)
②不可以手工调用构造函数,否则编译就会出错
t2.Time();//构造函数是根本就不能让你去手工用. or -> 来调用的!
③正常情况下,构造函数和析构函数都得声明为public,因为我们在创建一个类的对象时,编译器(外界)会为我们自动地调用这个函数去创建(初始化)对象。调用该对象完毕后,释放该类的对象时,编译器(外界)也会自动地为我们去调用析构函数去释放我们实例化了的对象(包括其占用的堆区空间or栈区空间)。如果说你把这2个函数弄成private私有的访问权限的话,那么你外界就无法访问了,那你还创建个der的对象?析构个der的对象?
④构造函数中如果有多个参数,那么我们创建对象时也要带上这些参数,并且你创建对象时写的这些参数的顺序要与构造函数中定义的形参顺序要保持一致!
二、多个构造函数:
一个类中,是可以存在多个构造函数的,包括普通构造函数 及其 重载,以及拷贝构造函数的重载,但,要注意:一个类中,的析构函数只能有一个!
仍然拿上一节的代码举例子: //默认构造函数1(若你不定义一个类时不定义任何构造函数,则编译器会为你自动地生成这个默认的构造函数1) Time(){} //默认构造函数2 Time():m_Hour(0), m_Minute(0), m_Second(0){} //构造函数(可以认为是默认构造函数之重载) Time(int h,int m,int s) :m_Hour(h), m_Minute(m), m_Second(s){} //拷贝构造函数(可以认为是默认构造函数之重载) Time(const Time& t); //析构函数(若你不定义一个类时不定义一个析构函数,则编译器会为你自动地生成这个默认的析构函数) ~Time(){}
有多个构造函数,就可以为类的对象之创建提供多种多样的初始化方法。
注意:要区分1构造函数、2拷贝构造函数、3拷贝赋值运算符函数这3种函数
1构造函数(创建对象时编译器自动调用的函数)
2拷贝构造函数(创建对象并给其赋值or初始化为另外一个对象时编译器自动调用的函数)
3拷贝赋值运算符函数(在类中对于operator=号函数的重载函数就是拷贝赋值运算符函数)
Time.h
#ifndef __TIME_H__ #define __TIME_H__ #includeusing namespace std; class Time { public: int m_Hour;//时 int m_Minute;//分 int m_Second;//秒 public: Time():m_Hour(0), m_Minute(0), m_Second(0){ cout << "调用了Time类的默认构造函数!" << endl; } Time(int h,int m,int s) :m_Hour(h), m_Minute(m), m_Second(s){ cout << "调用了Time类的有参构造函数!" << endl; } Time(const Time& t); Time& operator=(const Time& t); void reviseTime(int h, int m, int s); void showTime(); }; #endif
Time.cpp
#include"Time.h" void Time::reviseTime(int h, int m, int s) { this->m_Hour = h; this->m_Minute = m; this->m_Second = s; } void Time::showTime() { cout << this->m_Hour << endl; cout << this->m_Minute << endl; cout << this->m_Second << endl; } Time::Time(const Time& t) { cout << "调用了Time类的拷贝构造函数!" << endl; this->m_Hour = t.m_Hour;//时 this->m_Minute = t.m_Minute;//分 this->m_Second = t.m_Second;//秒 } Time& Time::operator=(const Time& t) { cout << "调用了Time类的拷贝赋值运算符重载函数!" << endl; this->m_Hour = t.m_Hour;//时 this->m_Minute = t.m_Minute;//分 this->m_Second = t.m_Second;//秒 return *this; }
main.cpp
#include#include"Time.h" using namespace std; void test() { //下面是:调用构造函数 的代码 Time t1 = Time(); Time t2; Time t3 = Time{}; Time t4 {}; Time t5 = {}; //5次调用构造函数 cout << "-----------------------" << endl; //下面是:调用拷贝构造函数 的代码 Time tt1 = t1; Time tt2(t2); Time tt3{t3}; Time tt4 = { t4 }; //4次调用拷贝构造函数 cout << "-----------------------" << endl; 下面是:调用拷贝赋值运算符重载函数 的代码 tt1 = t1; tt2 = t2; tt3 = t3; tt4 = t4; } int main(void) { test(); system("pause"); return 0; }
运行结果:
三、函数的默认参数:
函数的默认参数,顾名思义其实就在函数的形参表中对某个形参添加一个默认的初始值,这样do之后即便你在使用该函数时少传入该对应位置的参数也无所谓。因为编译器会默认给你这个函数这个位置的参数set为你写的默认值。
(写了某位置的默认参数后,调用该函数时根据你的需求,可以直接不传入该位置的实参,进而减少一些代码量)
写默认参数的规则:
①默认参数必须要
单独/只写在函数的原型声明中(且在函数的具体实现中不需要写,写了反而编译不通过) 或者
单独/只写在函数的定义中(也即具体实现中,当然,前提是你这个函数只有具体实现,而没有函数原型声明,一旦有函数原型声明就不能在定义中写默认参数,而要回归前面说的,只能在函数声明中写)
废话不多说,请看代码:
//main3.cpp #includeusing namespace std; void funcc(int a, int b, int c = 111); void func(int a, int b = 100) { //正确!因为func函数只有定义而没有函数原型声明,因此只在函数的定义中写默认参数也是可行的! cout << "func的调用!" << endl; cout << "func的a= " << a << " b= " << b << endl; } int main(void) { func(1);//a=1,b=100 funcc(1, 1);//a=1,b=1,c=111 system("pause"); return 0; } //void funcc(int a, int b, int c = 111);//错误!因为默认参数只可以写在函数的原型声明中! void funcc(int a, int b, int c ) { cout << "funcc的调用!" << endl; cout << "funcc的a= " << a << " b= " << b << " c= " << c << endl; }
②默认参数必须要尽量存在于函数形参列表的右侧,一旦从某个形参位置开始,给其set了默认值,则从该位置开始的后面的all的形参都必须set默认值,否则编译不通过!
void func1(int a = 100, int b,int c ) {} //错误×!默认形参 要尽量放在函数形参列表的 结尾 void func2(int a, int b = 100,int c ) {} //错误×!默认形参 要尽量放在函数形参列表的 结尾 void func3(int a = 100, int b = 100,int c ) {} //错误×!默认形参 要尽量放在函数形参列表的 结尾 void func4(int a, int b,int c = 100 ) {} //对√!默认形参 要尽量放在函数形参列表的 结尾 void func5(int a, int b = 100,int c = 100 ) {} //对√!默认形参 要尽量放在函数形参列表的 结尾 void func6(int a = 100, int b = 100,int c = 100 ) {} //对√!默认形参 要尽量放在函数形参列表的 结尾
写函数的默认参数时,我们会遇到一个小问题,我们不妨看一下如下代码:
//给定一个函数的2种写法 void showAB(int a,int b=0){ cout<< "a = " << a << "tb = " << b << endl; } void showAB(int a,int b){ cout<< "a = " << a << "tb = " << b << endl; } //在main.cpp中调用该函数 showAB(1,2); showAB(3);//缺省了第二个参数b
我这里提一个问题,那么到底showAB(1,2)和showAB(3)这2个函数的重载形式的调用语句到底是调用哪一个函数呢?怎么调用呢?
哈哈不用想了,这里直接给出结论:编译器对上面这样写的代码直接不通过!因为此时编译器根本就不知道应该去调用哪一个函数,到底是有默认值的showAB呢还是某一默认值的showAB呢?编译器傻傻分不清楚,so这给出一个非常重要的结论:对于函数而言,不能用默认值do为函数重载的条件,函数重载有且只有3个条件(或者说只有这3种情况才可以发生函数重载):
1参数类型不同
2参数个数不同
3参数顺序不同
(关于函数重载的话题在我之前的博客笔记中都有详细地记录总结,如果不懂的话可以去翻阅我的笔记总结或者去百度学一学哈)
这就给我们日后的coding过程留下一个好的编码规范,即:不要用默认参数去写函数重载,否则编译器都会“晕”!!!
四、隐式转换和explicit(显式类型转换)的用法:
未完待续。。。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)