网站建设需求分析,中韩双语网站制作价格,wordpress海外支付,淘宝网站开发实训报告目录文章目录#x1f4d5;再谈构造函数1. 构造函数体赋值2. 初始化列表3. explicit 关键字#x1f4d5;static 成员1. 概念2. static 成员变量3. static 成员函数#x1f4d5; 友元1. 友元函数2. 友元类#x1f4d5;内部类#x1f4d5;编译器优化#x1f4d5;再谈构造函数
1…
文章目录再谈构造函数1. 构造函数体赋值2. 初始化列表3. explicit 关键字static 成员1. 概念2. static 成员变量3. static 成员函数 友元1. 友元函数2. 友元类内部类编译器优化再谈构造函数
1. 构造函数体赋值
在创建对象时编译器通过调用构造函数给对象中各个成员变量一个合适的初始值。
class Date {
public:Date(int year, int month, int day){_year year;_month month;_day day;}
private:int _year;int _month;int _day;
};虽然上述构造函数调用之后对象中已经有了一个初始值但是不能将其称为对 对象中 成员变量 的初始化构造函数体中的语句只能将其称为赋初值而不能称作初始化。因为初始化只能初始化一次而构造函数体内可以多次赋值。
对于上面普通的成员变量不初始化当然没有关系但是比如下面的呢
main 函数中 定义了一个 aa 对象这是对这个对象的定义并没有定义其内部的成员变量。 引用、const修饰的成员变量、无默认构造的自定义类型成员。默认构造无参、全缺省、编译器自动生成的构造函数 对于这三类成员变量前两个都是要在定义的地方初始化第三个无法传参。所以必须要有一个 将成员变量初始化 的途径。
对于前两者当然可以考虑使用缺省值但是缺省值是在 C 11 才出现的那么之前是怎么解决的呢 C 使用初始化列表来完成该功能。
class B
{
public:B(int n):_b(0){cout B() endl;}private:int _b;
};class A
{
public:private:int _a11; // 声明int _a22;// 引用 、const修饰 、没有默认构造 的 自定义类型成员const int _x;int _ref; B bb;
};int main()
{const int t 10; // const 修饰只能初始化的时候给值A aa; // 对象的整体定义每个成员什么时候定义呢return 0;
}2. 初始化列表
初始化列表以一个冒号开始接着是一个以逗号分隔的数据成员列表每个成员变量后面跟一个放在括号中的初始值或表达式。
如下A 中给出了初始化列表可以简单的理解为在构造函数中间加一点东西。
按照初始化列表里面从上到下的顺序来看就是初始化 _x 为 1 、初始化 _ref 为 _a 的引用 、初始化bb 并且传参为 4自定义类型调用它的初始化列表。 然后函数体里面进行 _a1 、 _a2-- 操作。
class B
{
public:B(int n):_b(0){cout B() endl;}private:int _b;
};class A
{
public:A():_x(1), _ref(_a1) // 初始化成对 _a1 的引用, bb(4) // B 类的初始化列表有参数没有默认构造无参数需要传参必须这样写{_a1;_a2--;}
private:int _a11; // 声明int _a22;// 引用 、const修饰 、没有默认构造 的 自定义类型成员const int _x;int _ref; // 引用也要在定义的地方初始化B bb;
};int main()
{const int t 10; // const 修饰A aa; // 对象的整体定义return 0;
}成员在初始化列表中的顺序并不就是它们的初始化顺序。
如下代码运行结果是输出 1 随机值 。 因为初始化列表里面初始化顺序是 声明的顺序而 不是 初始化列表里面的顺序。如果是按照初始化列表里的顺序那么 _a1 先初始化为参数的值_a2 再初始化为 _a1 的值
class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout _a1 _a2 endl;}
private:int _a2;int _a1;
};
int main() {A aa(1);aa.Print();
}所以初始化列表有以下的一些注意点 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)。 类中包含以下成员必须放在初始化列表位置进行初始化 引用 成员变量const 成员变量自定义类型成员(且该类没有默认构造函数时) 尽量使用初始化列表初始化因为不管你是否使用初始化列表对于自定义类型成员变量一定会先使用初始化列表初始化。 成员变量在类中声明的次序就是其在初始化列表中的初始化顺序与其在初始化列表中的先后次序无关。 我们再来体会一下单参数和多参数的区别。
引用 那一篇文章有写到内置类型 转换成 自定义类型的引用必须要用 const 修饰因为是先将内置类型 放到 临时变量里面而临时变量具有常性所以为了避免权限放大要用 const 修饰。比如下面 main 函数里的第一行代码。
但是对于单个参数的构造函数而言如果也像下面注释一样先 构造 临时变量、再将 临时变量 拷贝构造给 a2未免过于麻烦所以有的编译器就会优化将这两个过程合二为一直接当成构造1 就相当于传过去的参数。
如下根据输出结果也可以看出没有调用拷贝构造。 class A {
public:A(int a):_a1(a){cout A(int a) endl;}A(const A a){cout A(const A a) endl;}private:int _a1;
};int main()
{const A a3 1; // 将 1 放到 A 类型的 临时变量里面。临时变量具有常性所以要const修饰// 单个参数A a1(1);A a2 1; // 隐式类型转换 将 1 当作参数调用初始化列表有了 A 类型的临时变量将临时变量拷贝给 a2return 0;
}C 一开始是不支持多参数的后来也支持了其用法和单参数类似只不过是使用的时候要用大的花括号。
class A {
public:A(int a,int b):_a1(a){cout A(int a) endl;}A(const A a){cout A(const A a) endl;}private:int _a1;
};int main()
{// 多个参数A b1(1, 2);A b2 { 2,2 }; // 多参数这样写const A b3 { 4,7 };return 0;
}
3. explicit 关键字
构造函数不仅可以构造 初始化对象对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数还具有类型转换的作用从上面两段代码也可以看出来将 内置类型转换成了自定义类型。
但是如果我们不需要构造函数的类型转换功能也是可以的只要用 explicit 修饰构造函数即可。
如下explicit 修饰之后依然强制类型转换会报错。 class A {
public:explicit A(int a):_a1(a){cout A(int a) endl;}A(const A a){cout A(const A a) endl;}private:int _a1;
};int main()
{// 如果不想让内置类型转换为自定义类型可以在初始化列表的函数前面用 explicit 修饰A a 1; // 报错return 0;
}
static 成员
1. 概念
声明为static的类成员称为类的静态成员用static修饰的成员变量称之为静态成员变量用static修饰的成员函数称之为静态成员函数。 静态成员变量一定要在类外进行初始化。 2. static 成员变量
如果要 实现一个类A计算程序中实例化了多少个A类型的对象。 最先想到的就是在全局设置一个变量每一次 构造 或者 拷贝 的时候该变量的值都 1 这样就可以计算实例化出了多少个对象。
但是有了 static 成员的知识储备就可以在类里面实现这样子能更好地体现“封装”的性质。
如下代码static 修饰的成员变量 count不属于某个具体的对象而是属于整个类。在类里面只是声明并不是定义并且在类外定义的时候是不要加 static 的。 但是如果成员变量在类里面被 private 修饰那么就不可以访问到。所以必须要用 public 修饰但是用 public 修饰之后又可以被任意访问修改。
这样子就又出现了一个问题如何才可以 不用public 修饰成员变量的情况下还可以访问到count呢这里要用static成员函数解决。
class A {
public:A(int a):_a1(a){count;cout A(int a) endl;}A(const A a){count;cout A(const A a) endl;}static int Size() // 静态成员函数没有 this 指针所以无法访问非静态的成员变量{return count;}public:int _a1;static int count; // 声明。此外这个成员变量不属于某个对象属于整个类
};int A::count0; // 初始化int main()
{// count 需要变成 public 才可以被外部访问到A aa(2);cout aa.count endl;cout A::count endl;return 0;
}3. static 成员函数
static 修饰的成员函数没有 this 指针。所以该函数内部不可以对 类 里面的非静态成员变量进行访问。
如下static 修饰的 Size() 函数只能访问静态成员变量 count 不可以访问非静态成员变量 _a1 因为该函数的参数是没有 this 指针的。 从某种程度上来说静态成员函数就是为了静态成员变量而创造的。
下面的代码通过 Size() 函数 返回 count 的值但是却不会对 count 进行任何修改因为返回值是拷贝进临时变量的。所以是最完美的结果“封装”性体现的很好。
class A {
public:A(int a):_a1(a){count;cout A(int a) endl;}A(const A a){count;cout A(const A a) endl;}static int Size() // 静态成员函数没有 this 指针所以无法访问非静态的成员变量{return count;}private:int _a1;static int count; // 声明。此外这个成员变量不属于某个对象属于整个类
};int A::count0; // 初始化int main()
{// 定义静态成员函数这个函数返回 count 的值cout A::Size() endl;cout A(1).Size() endl; // 匿名对象生命周期只在这一行return 0;
} static 成员的一些特性
静态成员为所有类对象所共享不属于某个具体的对象存放在静态区。静态成员变量必须在类外定义定义时不添加static关键字类中只是声明。类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问。静态成员函数没有隐藏的this指针不能访问任何非静态成员。静态成员也是类的成员受public、protected、private 访问限定符的限制。 友元 友元提供了一种突破封装的方式有时提供了便利。但是友元会增加耦合度破坏了封装所以友元不宜多用。 友元分为友元函数和友元类。 1. 友元函数
如下代码当我们想要在一个类里面对流输出进行重载的时候使用起来 是 d1 cout; 和常规使用方式反了。所以要么在iostream 标准库里面添加重载要么在外部写流输出重载前者显然不现实。但是在类的外部写流输出重载的时候无法访问类的成员变量也就无法输出对应的值。
此时就需要友元函数的介入。
class Date {
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}// d1 cout; - d1.operator(d1, cout); 不符合常规调用
// 因为成员函数第一个参数一定是隐藏的this所以d1必须放在的左侧ostream operator(ostream _cout){_cout _year - _month - _day endl;return _cout;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2023,2,8);d1cout;
} 友元函数可以直接访问类的私有成员它是定义在类外部的普通函数不属于任何类但需要在类的内部声明声明时需要加 friend 关键字。 如下代码在 Date 类里面 申明 流输入重载、流输出重载同时在两个函数的前面都加上 friend 。就代表这两个函数是 Date 类的友元函数可以访问 Date 类里面的成员变量。
两个函数的函数定义里面也可以看出来传入的第二个参数是 d 的引用但是 Date 类里面成员变量都是 private 修饰的那么即使是实例化的对象也无法访问。但是由于两个函数都是Date类的友元函数所以可以直接访问。
class Date {friend ostream operator(ostream _cout, const Date d);friend istream operator(istream _cin, Date d);
public:Date(int year 1900, int month 1, int day 1): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};ostream operator(ostream _cout, const Date d)
{_cout d._year - d._month - d._day;return _cout;
}
istream operator(istream _cin, Date d)
{_cin d._year;_cin d._month;_cin d._day;return _cin;
}int main()
{Date d;cin d;cout d endl;return 0;
}
友元函数的一些性质
友元函数可访问类的私有和保护成员但不是类的成员函数。友元函数不能用const修饰。友元函数可以在类定义的任何地方声明不受类访问限定符限制。一个函数可以是多个类的友元函数。友元函数的调用与普通函数的调用原理相同。
2. 友元类
联想友元函数的声明以及用法友元类也差不多。友元类的所有成员函数都可以是另一个类的友元函数都可以访问另一个类中的非公有成员。
如下在 Time 类里面声明Date 类是 Time 类的友元类那么 Date 类里面的任何一个成员函数都可以直接访问 Time 类里面的成员变量。
但是这样的关系是单向的—— Time 类 里面的成员函数不可以直接访问 Date 类的成员变量。
class Time {friend class Date; // 声明日期类为时间类的友元类则在日期类中就直接访问Time类中的私有成员变量
public:Time(int hour 0, int minute 0, int second 0): _hour(hour), _minute(minute), _second(second){}
private:int _hour;int _minute;int _second;
};class Date {
public:Date(int year 1900, int month 1, int day 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){
// 直接访问 Time 类私有的成员变量_t._hour hour;_t._minute minute;_t._second second;}
private:int _year;int _month;int _day;Time _t;
};关于友元类的一些性质
友元关系是单向的不具有交换性。 比如上述Time类和Date类在Time类中声明Date类为其友元类那么可以在Date类中直接 访问Time类的私有成员变量但想在Time类中访问Date类中私有的成员变量则不行。友元关系不能传递 如果C是B的友元 B是A的友元则不能说明C时A的友元。
内部类
如果一个类定义在另一个类的内部这个在内部的类就叫做内部类。内部类是一个独立的类它不属于外部类更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。 内部类其实就是外部类的友元类内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
比如下方的例子B类在A类的内部是A类的内部类。所以B天生就是A的友元类函数中可以通过A类的对象访问A类的成员变量比如 foo 函数种通过 a 访问 h 。 另外下方输出结果sizeof(aa)结果是 8并没有计算类型B里面的成员变量。这是因为虽然B是A的内部类但两个类是独立的仅仅相当于B被隐藏起来了而已。
class A {
private:static int k;int h;
public:class B { // B天生就是A的友元public:void foo(const A a){cout k endl;//OKcout a.h endl;//OK}};privateint _b1;double _b2;
};int A::k 1;int main()
{A::B b;b.foo(A());A aa;coutsizeof(aa)endl;return 0;
}使用内部类的情况也不多下面是它的特性
内部类可以定义在外部类的public、protected、private都是可以的。注意内部类可以直接访问外部类中的static成员不需要外部类的对象/类名。sizeof(外部类)外部类和内部类没有任何关系。
编译器优化
在函数传参和传返回值的过程中一般编译器会做一些优化减少对象的拷贝这个在一些场景下还是非常有用的。
如下代码在构造函数、拷贝构造、赋值重载、析构 这些函数体里面都使用了流输出只要调用函数就会打印相应的内容调试就可以知道某一行代码具体干了什么。
如下将内置类型的 1 强制转换成A 类型的 aa1按照之前理解的要先构造为临时变量然后临时变量拷贝构造给 aa1 。但是编译器会优化成直接构造1 就是传的参数。
func(aa1) 要将实参拷贝为形参所以调用一次拷贝构造。
func1(2); 先将 2 强制类型转换成 A 类型的临时变量再将临时变量拷贝构造为形参。所以要先构造再拷贝但是编译器优化为直接构造。 func1(A(3)); 先以3为参数进行构造成为实参再拷贝构造传给形参。优化为直接构造。
fuc2() 测试的是传引用由于传引用不存在把实参拷贝构造给形参所以没有优化的空间。
class A {
public:A(int a 0):_a(a){cout A(int a) endl;}A(const A aa):_a(aa._a){cout A(const A aa) endl;}A operator(const A aa){cout A operator(const A aa) endl;if (this ! aa) {_a aa._a;}return *this;}~A(){cout ~A() endl;}
private:int _a;
};void func1(A aa)
{}void func2(const A aa)
{}int main()
{A aa1 1; // 构造拷贝构造 -》 优化为直接构造func1(aa1); // 无优化func1(2); // 构造拷贝构造 -》 优化为直接构造func1(A(3)); // 构造拷贝构造 -》 优化为直接构造cout ---------------------------------- endl;func2(aa1); // 无优化func2(2); // 无优化func2(A(3)); // 无优化return 0;
}如下A 依然是上面的类一摸一样。改变的是两个外部函数返回值传A类型的并且函数内部定义A类型的对象。
直接调用 fuc3() 无疑先构造 aa 再拷贝构造出一个临时变量作为返回值。所以调用了构造、拷贝构造。但是没有优化的空间因为 func3() 函数内部 aa的 定义 和 返回是分开的。
A aa1func3(); 调用 fun3 的过程和上面一样先构造、再拷贝构造。生成的临时变量再拷贝构造给 aa1 所以经历了两次拷贝构造此时就会被优化成一个拷贝构造。
但是对于 aa2 而言aa2 已经定义好了aa2 func3(); 是赋值重载而不是拷贝构造所以无法优化。
对于 func4(); 它直接构造出一个匿名对象返回相当于构造和拷贝构造是连续的所以可以优化为构造。 A aa3 func4(); 除了调用 func4() 还多了将 func4() 生成的临时变量拷贝构造给 aa3 的过程所以是 构造、拷贝构造、拷贝构造。编译器会优化成 构造。
A func3()
{A aa;return aa; // 不可以优化因为分开的
}A func4()
{return A(); // 可以优化
}int main()
{func3();A aa1 func3(); // 拷贝构造拷贝构造 -- 优化为一个拷贝构造cout **** endl;A aa2;aa2 func3(); // 不能优化 因为是赋值重载cout --------------------------- endl;func4(); // 构造拷贝构造 -- 优化为构造A aa3 func4(); // 构造拷贝构造拷贝构造 -- 优化为构造return 0;
}
上面所提到的一些优化对于A类而言确实没有什么区别但是如果对于二叉树那些的呢多次拷贝开销很大所以要学会使用这些小技巧让程序更加优质
这里浅浅地总结一下
如果可以尽量 return 匿名对象。函数传参尽量传引用。如果要用一个对象来初始化另一个对象尽量使用拷贝构造而不是赋值重载。
类和对象部分到这一篇文章就算是完结啦有什么错误的地方欢迎指正 文章转载自: http://www.morning.wcghr.cn.gov.cn.wcghr.cn http://www.morning.nlywq.cn.gov.cn.nlywq.cn http://www.morning.ljllt.cn.gov.cn.ljllt.cn http://www.morning.sgjw.cn.gov.cn.sgjw.cn http://www.morning.mjbjq.cn.gov.cn.mjbjq.cn http://www.morning.kjyfq.cn.gov.cn.kjyfq.cn http://www.morning.qlwfz.cn.gov.cn.qlwfz.cn http://www.morning.ljdtn.cn.gov.cn.ljdtn.cn http://www.morning.fgqbx.cn.gov.cn.fgqbx.cn http://www.morning.tzjqm.cn.gov.cn.tzjqm.cn http://www.morning.ccyns.cn.gov.cn.ccyns.cn http://www.morning.jhrlk.cn.gov.cn.jhrlk.cn http://www.morning.hlrtzcj.cn.gov.cn.hlrtzcj.cn http://www.morning.hyryq.cn.gov.cn.hyryq.cn http://www.morning.llfwg.cn.gov.cn.llfwg.cn http://www.morning.rlxnc.cn.gov.cn.rlxnc.cn http://www.morning.cpmwg.cn.gov.cn.cpmwg.cn http://www.morning.rbbgh.cn.gov.cn.rbbgh.cn http://www.morning.bndkf.cn.gov.cn.bndkf.cn http://www.morning.bdqpl.cn.gov.cn.bdqpl.cn http://www.morning.fgxnb.cn.gov.cn.fgxnb.cn http://www.morning.lbrrn.cn.gov.cn.lbrrn.cn http://www.morning.lwgrf.cn.gov.cn.lwgrf.cn http://www.morning.jnptt.cn.gov.cn.jnptt.cn http://www.morning.ghxtk.cn.gov.cn.ghxtk.cn http://www.morning.fkcjs.cn.gov.cn.fkcjs.cn http://www.morning.xswrb.cn.gov.cn.xswrb.cn http://www.morning.pcshb.cn.gov.cn.pcshb.cn http://www.morning.madamli.com.gov.cn.madamli.com http://www.morning.wnjbn.cn.gov.cn.wnjbn.cn http://www.morning.rwlns.cn.gov.cn.rwlns.cn http://www.morning.cpqqf.cn.gov.cn.cpqqf.cn http://www.morning.tnthd.cn.gov.cn.tnthd.cn http://www.morning.zwdrz.cn.gov.cn.zwdrz.cn http://www.morning.rwlnk.cn.gov.cn.rwlnk.cn http://www.morning.ztqyj.cn.gov.cn.ztqyj.cn http://www.morning.sjpbh.cn.gov.cn.sjpbh.cn http://www.morning.ttvtv.cn.gov.cn.ttvtv.cn http://www.morning.deupp.com.gov.cn.deupp.com http://www.morning.jpfpc.cn.gov.cn.jpfpc.cn http://www.morning.poapal.com.gov.cn.poapal.com http://www.morning.stxg.cn.gov.cn.stxg.cn http://www.morning.gjlxn.cn.gov.cn.gjlxn.cn http://www.morning.slzkq.cn.gov.cn.slzkq.cn http://www.morning.tjndb.cn.gov.cn.tjndb.cn http://www.morning.wynnb.cn.gov.cn.wynnb.cn http://www.morning.rtlrz.cn.gov.cn.rtlrz.cn http://www.morning.crdtx.cn.gov.cn.crdtx.cn http://www.morning.mjbkp.cn.gov.cn.mjbkp.cn http://www.morning.dbfj.cn.gov.cn.dbfj.cn http://www.morning.dwkfx.cn.gov.cn.dwkfx.cn http://www.morning.rwlnk.cn.gov.cn.rwlnk.cn http://www.morning.kltsn.cn.gov.cn.kltsn.cn http://www.morning.wjyyg.cn.gov.cn.wjyyg.cn http://www.morning.mzbyl.cn.gov.cn.mzbyl.cn http://www.morning.xhddb.cn.gov.cn.xhddb.cn http://www.morning.dsxgc.cn.gov.cn.dsxgc.cn http://www.morning.yjdql.cn.gov.cn.yjdql.cn http://www.morning.rqlbp.cn.gov.cn.rqlbp.cn http://www.morning.bgnkl.cn.gov.cn.bgnkl.cn http://www.morning.xcbnc.cn.gov.cn.xcbnc.cn http://www.morning.dbrdg.cn.gov.cn.dbrdg.cn http://www.morning.xrwsg.cn.gov.cn.xrwsg.cn http://www.morning.tzjqm.cn.gov.cn.tzjqm.cn http://www.morning.plqqp.cn.gov.cn.plqqp.cn http://www.morning.jyknk.cn.gov.cn.jyknk.cn http://www.morning.qzsmz.cn.gov.cn.qzsmz.cn http://www.morning.ggcjf.cn.gov.cn.ggcjf.cn http://www.morning.dbjyb.cn.gov.cn.dbjyb.cn http://www.morning.bxqry.cn.gov.cn.bxqry.cn http://www.morning.lpppg.cn.gov.cn.lpppg.cn http://www.morning.xxwl1.com.gov.cn.xxwl1.com http://www.morning.rmqmc.cn.gov.cn.rmqmc.cn http://www.morning.zqdzg.cn.gov.cn.zqdzg.cn http://www.morning.hpjpy.cn.gov.cn.hpjpy.cn http://www.morning.hyxwh.cn.gov.cn.hyxwh.cn http://www.morning.jkcpl.cn.gov.cn.jkcpl.cn http://www.morning.ftznb.cn.gov.cn.ftznb.cn http://www.morning.lhztj.cn.gov.cn.lhztj.cn http://www.morning.bqpgq.cn.gov.cn.bqpgq.cn