北京公司模板网站好,西安网站开发培训多少钱,智慧团建系统官方网站,道滘东莞微信网站建设文章目录 一、再探构造函数二、类型转换2.1隐式类型转换2.2内置类型的类型转化2.3explicit关键字2.4多参数构造 三、static成员四、友元五、内部类内部类的特性 六、匿名对象 一、再探构造函数
类和对象(中)里介绍的构造函数#xff0c;使用的是赋值实现成员变量的初始化。而… 文章目录 一、再探构造函数二、类型转换2.1隐式类型转换2.2内置类型的类型转化2.3explicit关键字2.4多参数构造 三、static成员四、友元五、内部类内部类的特性 六、匿名对象 一、再探构造函数
类和对象(中)里介绍的构造函数使用的是赋值实现成员变量的初始化。而构造函数还有另一种初始化方法就是使用初始化列表。
格式
成员变量以冒号开始逗号分隔成员成员变量后面跟上一个括号其中放置一个初始值或表达式函数体内实现格外的功能。
class Myqueue
{
public:Myqueue():_pushst(10),_popst(10){}
private:Stack pushst;Stack popst;
}Myqueue的类编译器自动生成的构造函数调用了Stack的默认构造函数完成初始化。栈没有默认构造函数需要使用初始化列表实现自定义类型的默认构造函数。 必须使用初始化列表的类
没有默认构造的类类型成员变量、被const修饰的成员变量声明时必须初始化、被引用的成员变量声明引用时必须初始化
自定义类型成员变量没有默认构造想要调用它的构造需要传参在函数体内无法实现那就只能在初始化列表中实现传参。
#include iostream
using namespace std;
class Stack
{
public:Stack(int n): _a(new int[n]), _top(0), _capacity(n){}
private:int* _a new int;int _top 0;int _capacity 0;
};
class Myqueue
{
public:Myqueue():pushst(10),popst(10){cout Myqueue() endl;}
private:Stack pushst;Stack popst;
};栈没有实现默认构造函数只实现了构造函数在Myqueue中需要对两个栈类的成员变量进行初始化需要调用它的默认构造而栈类没有实现从而采用初始化列表。
在初始化列表中可以对这两个成员传参调用它们的构造函数从而实现初始化而若是在函数体内实现初始化是无法调用这两个栈类成员变量的构造函数。 每个成员变量在初始化列表中只能出现一次语法理解上初始化列表可以认为是每个成员变量定义初始化的地方 由于某些原因C规定必须给每一个成员变量找一个定义的地方比如被const修饰的变量、引用变量它们在定义的时候必须初始化。若是在函数体内实现每个成员可以出现多次不能确定那块位置是否为成员变量的初始化。
class A
{
public:A(int n 4): _n(new int[n]), _a(n), _b(n){}
private:int* _n;int _a;const int _b;
};若没有使用初始化列表完成 _a、_b成员变量的初始化编译时会出现这种报错。
error C2530: “A::_a”: 必须初始化引用 error C2789: “A::_b”: 必须初始化常量限定类型的对象
多次初始化编译器也会提示你某变量已经初始化了。 C支持在成员变量声明的地方给缺省值给没有在初始化列表显示初始化的成员变量使用
给缺省值的同时也对成员变量在初始化列表初始化
class B
{
public:B(int n 4): _n(new int[n]), _a(n), _b(n), _st(4){}
private:int* _n new int;int _a;const int _b 2024;Stack _st 10;
};可以发现运行结果都是按照初始化列表给定的值完成初始化。
给定缺省值不在初始化列表初始化编译器会自动调用缺省值使成员变量在初始化列表完成初始化。 尽量在初始化列表实现成员变量的初始化如果咱不写编译器也会将其放在初始化列表初始化。内置类型随机赋值或连续赋值自定义类型调用它的默认构造 初始化列表按照成员变量声明的顺序进行初始化所以在初始化列表中最好按照声明顺序进行定义别把顺序打乱了。
二、类型转换
2.1隐式类型转换
造构造函数中不仅可以构造初始化对象对于单个参数或者第一个参数无缺省值其余参数均有默认值的构造函数还具有类型转换的作用
class Date
{
public:Date(int year):_year(year){}
private:int _year;int _month 3;int _day 7;
};
int main()
{Date d1 1;return 0;
}上述代码将一个整形赋值给一个自定义类型这种逻辑很明显是错误的但是Date类实现了单个参数的构造函数使得整形1在赋值给d1对象时发生了隐式类型转换将整形1转换为Date类型从而进行赋值。
而实际的流程Date里实现的有通过整形为形参实现的单参数构造函数尝试使用一个int对象给一个Date对象赋值后编译器使用int对象调用构造函数生成一个Date类型的临时对象最后通过临时对象调用拷贝构造函数实现对Date对象的赋值。
构造函数–生成临时对象–调用拷贝构造–完成赋值
对于VS编译器调用构造函数 拷贝构造函数的过程会被优化为调用构造函数。这是编译器在效率上的优化提升不同版本编译器的优化效果和逻辑大不相同类如DEVC编译器可能就不会去进行优化。 注意编译器生成的临时对象具有常性
如下图前面提过隐式类型转换实际上通过调用构造函数生成临时对象而临时对象又去调用拷贝构造完成赋值的过程这里的临时对象具有常性下图的**普通d1对象尝试对一个具有常性的对象进行引用将会导致权限放大**从而引发编译器报错。 将d1对象使用const修饰后就不会出现这种问题
但这又引出了新的问题临时对象在调用完拷贝构造函数出了作用域它就会被销毁了而常性d1对象对临时对象使用了引用这导致d1对象对一块被销毁引用是野引用.
2.2内置类型的类型转化
对于内置类型的转换实际上也存在生成临时对象然后完成拷贝赋值的过程。
int main()
{//类似的还有int i 1;double j i;//ok~没问题隐式类型转换double j i//ok~这就有问题i生成的临时对象具有常性j想指向它导致权限放大编译器报错。const double j i;//使用const修饰权限平移。return 0;
}2.3explicit关键字
当不期望隐式类型转换的发生可以在构造函数前添加关键字 ecplicit
它的功能禁止使用隐式类型装换
、
2.4多参数构造
以上介绍的都是单参数的类型转换而多参数的构造实现后该如何赋值
class Date
{
public:Date(int year, int month 1, int day 1){_year year;_month month;_day day;}
private:int _year;int _month 3;int _day 7;
};
int main()
{Date d1 2024,10,1;//奇奇怪怪的赋值方法从来没有见过当然编译器也不支持这种写法return 0;
}多参数的类型装换需要使用 {}花括号 Date d1 {2024, 10, 1};C11才开始支持
int main()
{Date d1 {2024, 10, 1};return 0;
}同理若是多参数的构造函数不期望支持隐式类型转化使用关键字 explicit修饰即可。
三、static成员
用static修饰的成员变量称为静态成语变量静态成员变量一定要在类外面进行初始化静态成员变量为所有类成员共享不属于某个具体的对象不存在类中存在静态区
class pp
{
private:int _z;int _y;static int _x;
};
int pp::_x 1;用static修饰的成员函数称之为静态成员函数静态成员函数没有this指针非静态的成员函数可以访问任意位置的成员变量和成员函数 静态成员函数只能访问静态成员变量和静态成员函数它没有this指针
class pp
{
public:pp(int z 3, int y 2):_z(z),_y(y){}static int Get_x(){_z;return _x;}void Fun(){_x;_y;}
private:int _z;int _y;static int _x;
};
int pp::_x 1;类如上述静态成员函数它被static修饰后就没有this指针在 Get_x 函数内调用就回引起编译器报错。 error C2597: 对非静态成员“pp::_z”的非法引用
Fun函数就不会有什么问题它并不是静态成员函数函数内部使用this指针和静态成员变量都是正确的。 静态成员也受 private、public、protected访问限定符的限制 突破类域就可以访问静态成员变量可以通过 类名::静态成员 或者 对象.静态成员来访问静态成员变量、函数。 class pp
{
public:pp(int n 1){_z n;_y n;}
private:int _z;int _y;
public:static int _x;
};
int pp::_x 10;
int main()
{pp p;cout p._x endl;cout pp::_x endl;return 0;
}若静态成员变量收限定符的限制无法通过类名或对象类进行访问此时可以写一个get函数来获取静态成员函数的大小。 静态成员变量不能在声明位置给缺省值初始化这个缺省值是构造函数初始化列表使用的而静态成员变量不属于某个对象不能走构造函数初始化列表。
class A
{
private:static int _count;
}
int A::_count 1;int main()
{A a1;A a2;A a3;//a1、a2、a3都可以访问cout A::_count endl;cout a1._count endl;//没有被private修饰两种突破类域的方法a1.GetCount();//或者在类中实现获取count的函数。return 0;
}四、友元
友元是一种用于突破类访问限定符封装的方式通过它就可以使外部函数访问类的私有和包含成员。而友元分为友元函数、友元类。在函数声明或类声明的位置前面加friend并将其放在一个类中。
友元函数/类可以访问类的私有和保护成员友元函数仅仅是一种声明他不是类的成员。
class Date
{friend ostream operator(ostream out, const Date d);
public:Date(int year, int month, int day){_year year;_month month;_day day;}
private:int _year;int _month;int _day;
};
ostream operator(ostream out, const Date d)
{cout d._year 年 d._month 月 d._day 日 endl;
}友元函数/类可以在类定义的任何地方使用不受访问限定符限制一个函数可以是多个类的友元
#include iostream
using namespace std;class hh;//前置声明//若不做声明pp中的友元函数声明不认识hh类
class pp
{friend void Fun(const pp p, const hh h);
public:pp(int n 2): _a(n), _b(n){}private:int _a;int _b;
};
class hh
{friend void Fun(const pp p, const hh h);
public:hh(int n 5): _m(n), _n(n){}private:int _m;int _n;
};
void Fun(const pp p, const hh h)
{cout h._m endl;cout p._a endl;
} 友元类的关系是单向的A类是B类的友元B类不是A类的友元。 就好比张三和李四是好盆友李四和王五是好朋友但这并不代表王五是张三的好朋友他们两个认不认识还不一定 友元类的关系不能传递A类是B类的友元B类是C类的友元但A类不是C类的友元。 类似友人B找了两个女朋友分别是友人A、友人C这时候友人B脚踏两只船。 而友人B可不希望两个女朋认识友人B都给女朋友说我只爱你一个人然而呢~某一天友人B和友人A出去逛商场的时候恰巧碰见了友人C诶哟我去这个场面给位自己想象吧 ~嘿嘿 友元的存在增加了耦合度破坏了封装不宜多用。
五、内部类 如果一个类定义在另一个内的内部那就将这个类称之为内部类 概念
内部类是一个独立的类。内部类与定义在全局相比内部类受到了外部类类域限制和访问限定符的限制外部类定义的对象中不包含内部类。
下列在类M中定义了一个N类那这个N类就是内部类
class M
{
public:M(int n 3):_a(n),_b(n){}
private:int _a;int _b;public:class N{public:void Fun(const M m){cout m._a endl;}private:int _x;};
};
int main()
{M m;cout sizeof(m) endl;return 0;
}内部类天生就是外部类的友元内部类可以通过外部类的对象参数来访问外部类中所有成员但外部类不是内部类的友元 N类包在M类里面若需要单独实例化N类将会收到外部类类域的限制此时需要使用域作用限定符 ::
内部类的特性
内部类定义在外部类的public、private、protected都是可以的
class pp
{
public:class A{private:int _a;};
private:class B{private:int _b;};
protected:class C{private:int _c;};
};由于内部类是外部类的友元内部类在访问外部类的不需要加 类名/对象名 内部类并不会占用外部类的空间容量使用sizeof计算外部类的大小不会与内部类有关 若两个类的关系紧密相连B类的功能主要提供给A类使用就可以考虑将B类实现为A类的内部类。若给内部类使用访问限定符修饰private/protected那这个内部类就是外部类的专属其余类无法使用。
六、匿名对象
匿名对象没有类名的对象。语法类名()
可以发现在下列代码中定义了两个对象一个有名字一个没有名字。没有名字的对象就称为匿名对象。
#include iostream
using namespace std;
class C
{
public:C(int n 10){cout 调用构造 endl;_c n;}~C(){cout 调用析构 endl;}int ret() { return _c; }
private:int _c;
};
int main()
{C c1(66);C(6);return 0;
}首先介绍匿名对象特别重要的一个特性匿名对象的声明周期只有当前行当编译器读到下一行代码时匿名对象就会被自动销毁了。 没有对象名的匿名对象也可以调用成员函数 匿名对象的引用 匿名对象具有常性被引用需要将const修饰这样做它的生命周期被延长了不会即用即销毁在程序运行结束后才会销毁。const A r A(); 但它被引用后而匿名对象的空间被销毁导致野引用这在语法上是不允许的反之没有使用const进行修饰在编译时会引起编译器的报错 匿名对象作函数缺省值
class A
{
public:A(int n 2):_a(n){}~A(){cout ~A() endl;}
private:int _a;
};
void fun(A aa A(1))//函数缺省值给匿名对象
{}
int main()
{A();//这是一个匿名对象。没有参数也必须加上括号A(10);//这是一个匿名对象。return 0;
}