滁州网站建设czesou,上不了建设银行网站,中国石油大学网页设计与网站建设,网页传奇装备构造函数
1 构造函数体赋值 在创建对象时#xff0c;编译器通过调用构造函数#xff0c;给对象中各个成员变量一个合适的初始值 虽然对象中已经有了一个初始值#xff0c;但是不能将其称为对对象中成员变量的初始化
构造函数体中的语句只能将其称为赋初值#xff0c;而…构造函数
1 构造函数体赋值 在创建对象时编译器通过调用构造函数给对象中各个成员变量一个合适的初始值 虽然对象中已经有了一个初始值但是不能将其称为对对象中成员变量的初始化
构造函数体中的语句只能将其称为赋初值而不能称作初始化。因为初始化只能初始 化一次而构造函数体内可以多次赋值
2 初始化列表
初始化列表以一个冒号开始接着是一个以逗号分隔的数据成员列表每个成员变量后面跟 一个放在括号中的初始值或表达式
初始化列表是每个成员定义的地方
注意 1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次) 2. 类中包含以下成员必须放在初始化列表位置进行初始化 引用成员变量因为引用必须初始化那就需要在定义时初始化 const成员变量const成员不能被修改在定义的时候就要给值否则就不能给值了 自定义类型成员(且该类没有默认构造函数时)
3 尽量使用初始化列表初始化因为不管你是否使用初始化列表对于自定义类型成员变量 一定会先使用初始化列表初始化 4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序与其在初始化列表中的先后 次序无关
3 explicit关键字
构造函数不仅可以构造与初始化对象对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数还具有类型转换的作用
1. 单参构造函数没有使用explicit修饰具有类型转换作用 explicit修饰构造函数禁止类型转换 2. 虽然有多个参数但是创建对象时只有第一个参数没有默认值那么若没有使用explicit修饰 具有类型转换作用
class Date
{
public:Date(int year, int month 1, int day 1):_year(year),_month(month),_day(day){}Date operator(const Date d)//赋值重载-可以不写默认生成够用{if (this ! d){_year d._year;_month d._month;_day d._day;}return *this;}
private:int _year;int _month;int _day;
};int main()
{Date d(2);d 2023;//发生类型转换return 0;
}
用整型2023去赋值给日期类对象实际上会用2023去构造一个临时对象再用这个临时对象去赋值日期类对象
但若用explicit修饰构造函数后禁止了类型转换会报错 static成员
1 概念
声明为static的类成员称为类的静态成员用static修饰的成员变量称之为静态成员变量用static修饰的成员函数称之为静态成员函数。静态成员变量一定要在类外进行初始化
2 特性
1. 静态成员为所有类对象所共享不属于某个具体的对象存放在静态区 2. 静态成员变量必须在类外定义定义时不添加static关键字类中只是声明 3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问 4. 静态成员函数没有隐藏的this指针不能访问任何非静态成员 5. 静态成员也是类的成员受public、protected、private 访问限定符的限制
累积创建了多少个对象调用构造函数的次数调用拷贝构造函数的次数
正在使用的还有多少个对象调用构造函数的次数调用拷贝构造函数的次数-调用析构函数的次数
对象创建会自动调用构造函数若是对象是用已存在对象初始化地创建那就会自动调用拷贝构造函数对象生命周期结束了会自动调用析构函数所以以上两种问题可以通过统计构造函数、拷贝构造函数、析构函数的调用次数轻松解决 class A
{
public:A(){n;m;}A(const A a){n;m;}~A(){m--;}static int GetN()//没有了this指针在类外可以用类名函数名直接调用不用通过对象.函数名调用{return n;}static int GetM()//没有了this指针{return m;}
private:static int n;//累计创建的对象 静态成员变量属于所有A对象属于整个类static int m;//正在使用的对象
};int A::n 0;//在类外定义
int A::m 0;int main()
{A a1;A a2;A a3(a1);A();//匿名对象生命周期只在这一行cout A::GetN() A::GetM() endl;cout a1.GetN() a1.GetM() endl;return 0;
} 友元
友元提供了一种突破封装的方式有时提供了便利。但是友元会增加耦合度破坏了封装所以友元不宜多用
友元分为友元函数和友元类
1 友元函数
在实际中若要重载operator但是我们会发现没办法将operator重载成成员函数因为ostream类型的对象要是第一个参数但是在类的成员函数中第一个参数是this指针要是硬重载成为成员函数也是ok的但是使用起来会非常别扭比如
class A
{
public:ostream operator(ostream out){out _a endl;return out;}
private:int _a0;
};int main()
{A a;a cout;return 0;
} 是可以运行起来的但是使用很别扭所以我们要将operator重载成全局函数这样两个参数的位置就可以由我们来分配了但又会导致类外没办法访问成员此时就需要友元来解决
友元函数可以直接访问类的私有成员它是定义在类外部的普通函数不属于任何类但需要在 类的内部声明声明时需要加friend关键字
class A
{friend ostream operator(ostream out, const A a);friend istream operator(istream in, A a);private:int _a0;
};int main()
{A a;cin a;cout a endl;return 0;
}istream operator(istream in, A a)
{in a._a;return in;
}ostream operator(ostream out,const Aa)
{out a._a endl;return out;
} 注意
友元函数可访问类的私有和保护成员但不是类的成员函数 友元函数不能用const修饰因为没有this指针 友元函数可以在类定义的任何地方声明不受类访问限定符限制 一个函数可以是多个类的友元函数 友元函数的调用与普通函数的调用原理相同
2 友元类
1 友元类的所有成员函数都可以是另一个类的友元函数都可以访问另一个类中的私有成员 2 友元关系是单向的不具有交换性 比如现在有Time类和Date类在Time类中声明Date类为其友元类那么可以在Date类中可以直接访问Time类的私有成员变量但不可以在Time类中访问Date类中私有成员变量 3 友元关系不能传递 如果C是B的友元 B是A的友元不能说明C是A的友元 4 友元关系不能继承
class Time
{friend class Date;
public:Time(int hour 1, int minute 1, int second 1):_hour(hour),_minute(minute),_second(second){}
private:int _hour;int _minute;int _second;
};class Date
{
public:Date(int year 1, int month 1, int day 1):_year(year),_month(month),_day(day){}void TimeSet(int hour, int minute, int second){_t._hour hour;//直接访问Time类的私有成员_t._minute minute;_t._second second;}
private:int _year;int _month;int _day;Time _t;
};
3 内部类
如果一个类定义在另一个类的内部这个内部类就叫做内部类。内部类是一个独立的类 它不属于外部类更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越 的访问权限
内部类就是外部类的友元类
特性
1. 内部类可以定义在外部类的public、protected、private都是可以的 2. 注意内部类可以直接访问外部类中的static成员不需要外部类的对象/类名 3. sizeof(外部类)外部类和内部类没有任何关系
class A
{
public:class B//B天生就是A的友元{public:void Print(const Aa){cout i endl;cout a._m endl;}};
private:static int i;int _m 3;
};int A::i 4;int main()
{A::B b;b.Print(A());return 0;
}
匿名对象
匿名对象的生命周期只有一行
匿名对象这样定义
class A
{
private:int _a;
};int main()
{A();//匿名对象return 0;
}
平常我们要调用类的成员函数需要先写一行创建类对象然后再写一行用对象去调用
class A
{
public:void Print(){cout _a endl;}
private:int _a 9;
};int main()
{A a;a.Print();return 0;
}
此时匿名对象的作用就体现出来了
class A
{
public:void Print(){cout _a endl;}
private:int _a 9;
};int main()
{A().Print();return 0;
} 拷贝对象时的一些编译器优化 在传参和传返回值的过程中一般编译器会做一些优化以减少对象的拷贝
以下面代码为示例 class A
{
public:A(int a 0):_a(a){cout A(int a 0) endl;}A(const A a){_a a._a;cout A(const A a) endl;}A operator(const A a){cout A operator(const A a) endl;if (this ! a){_a a._a;}return *this;}~A(){cout ~A()endl;}private:int _a;
};void f1(A aa)
{}A f2()
{A aa;return aa;
}
1 传值传参
int main()
{// 传值传参A aa1;//调用构造函数f1(aa1);//传值传参调用拷贝构造用已存在的对象去初始化地创建新对象return 0;
} 2 传值返回 A f2()
{A aa;//调用构造函数return aa;//aa作为局部变量出了函数作用域就销毁了那么会生成一个临时变量用aa去拷贝构造生成返回的不是aa是临时对象
}
int main()
{// 传值返回f2();return 0;
}3 隐式类型 void f1(A aa)
{}
int main()
{// 隐式类型连续构造拷贝构造-优化为直接构造f1(1);return 0;
}用整型1给A类对象aa传参会发生隐式类型转换
首先会生成一个临时对象用1去构造这个临时对象
其次用这个临时对象去拷贝构造aa
连续的步骤是构造拷贝构造
连续的构造编译器会优化直接合并成一步构造 4 表达式传参
void f1(A aa)
{}
int main()
{// 一个表达式中连续构造拷贝构造-优化为一个构造f1(A(2));return 0;
} 用匿名对象给A类对象aa传参
首先对匿名对象调用构造函数初始化为2
其次用匿名对象拷贝构造生成对象aa
连续的步骤构造拷贝构造
编译器做优化直接合并为调用一次构造 5
A f2()
{A aa;//调用构造函数return aa;//aa作为局部变量出了函数作用域就销毁了那么会生成一个临时变量用aa去拷贝构造生成返回的不是aa是临时对象
}int main()
{// 一个表达式中连续拷贝构造拷贝构造-优化一个拷贝构造A aa2 f2();return 0;
}
1 调用f2函数首先创建对象aa调用了构造函数
2 f2调用结束要返回aa对象要生成一个临时对象用aa拷贝构造生成
3 临时对象带回返回值临时对象拷贝构造对象aa2
连续的步骤步骤23 即拷贝构造拷贝构造
编译器优化直接合并为调用一次拷贝构造
在aa销毁前用aa拷贝构造aa2 6
A f2()
{A aa;//调用构造函数return aa;//aa作为局部变量出了函数作用域就销毁了那么会生成一个临时变量用aa去拷贝构造生成返回的不是aa是临时对象
}
int main()
{A aa1;// 一个表达式中连续拷贝构造赋值重载-无法优化aa1 f2();return 0;
}
1 创建aa2调用构造
2 调用f2函数创建aa调用析构
3 返回局部变量aa先生成临时变量aa拷贝构造临时对象
4 临时对象赋值给对象aa1
连续步骤34 即拷贝构造赋值重载
由于不属于一个派系所以编译器无法优化
7
int main()
{A aa2 1;//类型转换return 0;
}
首先会用1去构造一个临时对象再用这个临时对象去拷贝构造aa2
连续的构造拷贝构造编译器直接优化成构造 8
nt main()
{A aa3 A(2);return 0;
}
首先用2去构造匿名对象然后用它去拷贝构造aa3
连续的构造拷贝构造编译器直接优化为构造 然后再调用析构函数析构aa3对象