当前位置: 首页 > news >正文

如何写网站优化目标西宁整站优化

如何写网站优化目标,西宁整站优化,wordpress视频外部储存,本手机原有微信网站再探构造函数 在实现构造函数时#xff0c;对成员变量进行初始化主要有两种方式#xff1a; 一种是常见的在函数体内赋值进行初始化#xff1b;另一种则是通过初始化列表来完成初始化。 之前我们在构造函数中经常采用在函数体内对成员变量赋值的方式来给予它们初始值。例如对成员变量进行初始化主要有两种方式 一种是常见的在函数体内赋值进行初始化另一种则是通过初始化列表来完成初始化。 之前我们在构造函数中经常采用在函数体内对成员变量赋值的方式来给予它们初始值。例如 class Date { public:// 构造函数Date(int year 1, int month 1, int day 1){_year year;_month month;_day day;} private:int _year;int _month;int _day; }; 不过需要特别留意的是尽管调用上述这样的构造函数后对象中的每个成员变量都能获得一个初始值但构造函数内的这些语句准确来讲只能算是赋初值而非真正意义上的初始化。这是因为初始化原则上只能进行一次而在构造函数体内却能够对成员变量进行多次赋值操作。就像下面这个修改后的Date类构造函数示例 class Date { public:// 构造函数Date(int year 1, int month 1, int day 1){_year year; // 第一次赋值_year 2024; // 第二次赋值//..._month month;_day day;} private:int _year;int _month;int _day; }; 初始化列表的好处在于能 满足引用、const 及无默认构造函数的类类型等特定成员变量的初始化要求、提高自定义类型成员变量的初始化效率明确成员变量按照在类中声明顺序进行初始化。 初始化列表 一、初始化列表的基本形式 初始化列表的使用方式是以一个冒号开始接着是一个以逗号分隔的数据成员列表每个 “成员变量” 后面跟一个放在括号中的初始值或表达式。以下是一个简单示例 class Date { public:Date(int year 1, int month 1, int day 1):_year(year), _month(month), _day(day){} private:int _year;int _month;int _day; }; 二、初始化列表的语法规则 每个成员变量在初始化列表中只能出现一次。从语法理解上来说初始化列表可以看作是每个成员变量定义初始化的地方。初始化列表中按照成员变量在类中声明顺序进行初始化与成员在初始化列表出现的先后顺序无关。不过建议在编写代码时让成员变量在类中的声明顺序和在初始化列表中的顺序保持一致这样可以使代码的逻辑更加清晰便于理解和维护。 面试题 下⾯程序的运⾏结果是什么 A. 输出1 1 B. 输出2 2 C. 编译报错 D. 输出1 随机值 E. 输出1 2 F. 输出2 1 #includeiostreamusing namespace std;class A { public:A(int a):_a1(a), _a2(_a1){}void Print() {cout _a1 _a2 endl;}private:int _a2 2;int _a1 2; };int main() {A aa(1);aa.Print(); } 分析这道题需明确初始化顺序按类中变量声明顺序非初始化列表顺序。 先说选项“存在 2” 不可选因已显式初始化一旦这样做了那就和之前给变量设置的缺省值就没关系选 “A” 也不可选初始化是按变量声明顺序而非初始化列表顺序。 具体本题初始化过程先初始化_a2虽传参 a 值为 1但按声明顺序此时其不受影响_a2 (_a1) 这步因_a1 未初始化到传值 1相当于用不确定值类似随机值初始化所以_a2 初始化后是随机值。之后用传进的 1 初始化_a1。 综上本题结果选 D。建议写代码时让成员变量声明顺序与初始化列表顺序一致可使代码逻辑清晰、不易出错。 三、必须使用初始化列表初始化的情况 以下几种类型的成员变量必须放在初始化列表位置进行初始化否则会导致编译报错 引用成员变量引用类型的变量在定义时就必须被赋予一个初始值所以在类中的引用成员变量必须通过初始化列表来初始化。例如 int a 10; int b a; // 创建时就初始化class ClassWithRef { public:ClassWithRef(int refValue) :refMember(refValue) {} private:int refMember; }; const 成员变量被const修饰的变量同样需要在定义时就给定初始值因此也必须使用初始化列表进行初始化。比如 int a 10; int b a; // 创建时就初始化class ClassWithRef { public:ClassWithRef(int refValue) :refMember(refValue) {} private:int refMember; }; 没有默认构造的类类型成员变量若一个类没有默认构造函数默认构造函数是指不用传参就可以调用的构造函数包括编译器自动生成的构造函数、无参的构造函数以及全缺省的构造函数那么在实例化该类对象作为另一个类的成员变量时就必须使用初始化列表对其进行初始化。例如 class A { public:A(int val) {_val val;} private:int _val; };class B { public:B() :_a(2024) {} private:A _a; }; 在上述示例中A类没有默认构造函数所以在B类的构造函数中对于成员变量_a类型为A就必须通过初始化列表来初始化。 总结 这些成员变量在定义或者实例化相关情境下自身都存在一些特殊要求使得它们必须在特定时刻就被赋予初始值。 对于引用成员变量其类型特性决定了定义时就得有初始值const 成员变量由于被 const 修饰按规定一开始就要确定值而没有默认构造函数的类类型成员变量在作为另一个类的成员被实例化时因为无法依靠默认构造函数来完成初始化 所以都需要通过初始化列表这种方式来明确地给它们设定初始值否则程序在编译阶段就会报错无法正常进行下去。  四、C11 关于成员变量缺省值的规定 C11 支持在成员变量声明的位置给缺省值这个缺省值主要是供那些没有显式在初始化列表初始化的成员使用的。例如 class SomeClass { public:SomeClass() {} private:int memberVariable 10; // 这里给成员变量声明了缺省值 }; 在上述SomeClass类中成员变量memberVariable声明时被赋予了缺省值10当在构造函数中没有显式通过初始化列表对其进行初始化时就会使用这个缺省值。 尽量使用初始化列表初始化成员变量原因如下 无论是否在初始化列表中显式地对成员变量进行初始化每个成员变量实际上都要走初始化列表这个流程。如果某个成员在声明位置给了缺省值那么初始化列表会用这个缺省值来初始化该成员。对于没有在初始化列表中显式初始化的情况 内置类型成员如果没有给缺省值对于没有显式在初始化列表初始化的内置类型成员是否初始化取决于编译器C 并没有对此作出明确规定。自定义类型成员对于没有显式在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数如果该成员类型没有默认构造函数则会导致编译错误。 class Time { public:Time(int hour 0) {_hour hour;} private:int _hour; };class Test { public:// 使用初始化列表Test(int hour):_t(12) // 调用一次Time类的构造函数{} private:Time _t; };// 对比不使用初始化列表的情况 class TestWithoutList { public:TestWithoutList(int hour) {Time t(hour);_t t;} private:Time _t; }; 在上述示例中通过对比Test类使用初始化列表和TestWithoutList类不使用初始化列表可以看出不使用初始化列表在实例化TestWithoutList类对象时会多进行一些不必要的构造函数调用和赋值操作从而影响效率。 五、关于初始化列表的总结要点 无论是否显式地写出初始化列表每个构造函数实际上都拥有一个隐含的初始化列表。无论是否在初始化列表中显式地对成员变量进行初始化每个成员变量都要走初始化列表这个初始化流程。 综上所述初始化列表在构造函数对成员变量的初始化过程中扮演着重要的角色理解并正确运用它对于编写高效、正确的 C 代码至关重要。 类型转换 隐式类型转换是 C 中由编译器自动完成的一种类型转换机制通常在不同类型的变量之间进行操作时发生。 支持隐式转换单个参数构造函数除构造初始化对象外还支持隐式转换如Date类单参构造函数Date d1 2021可将2021转为Date类对象。早期处理早期编译器遇Date d1 2021先构造临时对象Date tmp(2021)再拷贝构造d1。现代优化现在编译器遇此代码按Date d1(2021)处理仍属隐式转换。对比常见情况类似int a 10; double b a的常见隐式转换会构建临时变量传值函数返回局部变量值成功也因隐式转换产生的临时变量未销毁。 一、类类型相关隐式转换 1.内置类型到类类型 C 支持内置类型隐式转换为类类型对象但需要有以相关内置类型为参数的构造函数。例如我们有一个A类其构造函数接受一个int参数 class A { public:A(int a1) :_a1(a1) {} private:int _a1; };当我们编写A aa1 1;时编译器会自动调用A类的这个构造函数将1转换为A类的对象aa1。这里实际上发生了隐式类型转换先构造了一个临时的A类对象然后再用这个临时对象拷贝构造aa1在一些现代编译器中可能会进行优化直接构造aa1。 完整使用代码水果篮 class FruitBasket { public:FruitBasket(int numFruits) : _numFruits(numFruits) {} private:int _numFruits; }; int main() {// 5隐式转换为FruitBasket类对象FruitBasket myBasket 5; return 0; } 2.类类型之间 类类型的对象之间也可以进行隐式转换这同样需要相应的构造函数支持。假设有A类和B类B类有一个以A类对象为参数的构造函数 class B { public:B(const A a) :_b(a.Get()) {} private:int _b; };当我们编写B b aa3;假设aa3是A类的对象时就会发生从A类对象到B类对象的隐式转换调用B类的相应构造函数来创建b对象。 完整使用代码  class FruitBasket { public:FruitBasket(int numFruits) : _numFruits(numFruits) {}int GetNumFruits() { return _numFruits; } private:int _numFruits; }; class FruitShopInventory { public:FruitShopInventory(const FruitBasket basket) : _stock(basket.GetNumFruits()) {} private:int _stock; }; int main() {FruitBasket myBasket 8;// myBasket隐式转换为FruitShopInventory类对象FruitShopInventory inventory myBasket; return 0; } 3.控制隐式转换explicit 如果我们不想让这种隐式类型转换发生可以在构造函数前面加上explicit关键字。例如如果将A类的构造函数修改为explicit A(int a1)那么A aa1 1;这样的隐式转换语句将不再被允许编译器会报错。 class SpecialFruitBasket { public:explicit SpecialFruitBasket(int numFruits) : _numFruits(numFruits) {} private:int _numFruits; }; int main() {// 加explicit后此行隐式转换报错应改为SpecialFruitBasket specialBasket(3);// SpecialFruitBasket specialBasket 3; return 0; } 二、一般隐式转换情况及问题 在基本类型之间隐式转换也很常见。例如当我们进行int和double的运算时如int a 10; double b 5.5; double c a b;编译器会自动将int类型的a转换为double类型以便与b进行运算。然而隐式转换虽然方便但也可能会带来一些问题比如精度丢失。当double转换为int时小数部分会被截断这可能导致数据不准确。 int main() {int numApples 5;double pricePerApple 2.5;// numApples隐式转换为double计算总价double totalPrice numApples * pricePerApple; double totalMoney 12.8;// totalMoney隐式转换为int算能买苹果个数可能精度丢失int numBoughtApples totalMoney / pricePerApple; return 0; } 再举一个结合例子 #includeiostream using namespace std;// 定义类A class A { public:// 构造函数若声明为explicit则不再支持隐式类型转换// 这里未声明为explicit所以支持隐式类型转换// 接受一个int参数的构造函数用于初始化成员变量_a1// explicit A(int a1)A(int a1):_a1(a1) // 在初始化列表中将传入的参数a1赋值给成员变量_a1{}// 接受两个int参数的构造函数分别用于初始化成员变量_a1和_a2// explicit A(int a1, int a2)A(int a1, int a2):_a1(a1) // 在初始化列表中将第一个参数a1赋值给成员变量_a1, _a2(a2) // 在初始化列表中将第二个参数a2赋值给成员变量_a2{}// 成员函数用于输出成员变量_a1和_a2的值void Print(){cout _a1 _a2 endl;}// 常成员函数用于返回成员变量_a1和_a2的和int Get() const{return _a1 _a2;}private:int _a1 1; // 定义并初始化成员变量_a1默认值为1int _a2 2; // 定义并初始化成员变量_a2默认值为2 };// 定义类B class B { public:// 构造函数接受一个A类的常引用作为参数// 通过调用传入的A类对象的Get函数获取值用于初始化成员变量_bB(const A a):_b(a.Get()){}private:int _b 0; // 定义并初始化成员变量_b默认值为0 };int main() {// 1. 创建对象aa1并涉及隐式类型转换// 构造一个A的临时对象再用这个临时对象拷贝构造aa1// 编译器遇到连续构造 拷贝构造的情况会优化为直接构造// 这里利用A类接受一个int参数的构造函数将整数1隐式转换为A类对象并赋值给aa1A aa1 1;aa1.Print(); // 调用aa1的Print函数输出_a1和_a2的值// 2. 创建一个指向临时A类对象的常引用aa2// 同样是利用隐式类型转换将整数1转换为临时的A类对象并将该临时对象的引用赋值给aa2const A aa2 1;// 3. 创建对象aa3并使用多参数初始化C11之后支持的方式// 使用接受两个int参数的构造函数创建A类对象aa3并通过初始化列表初始化成员变量_a1和_a2A aa3 { 2, 2 };// 4. 从A类对象aa3隐式转换创建B类对象b// 原理和前面将整数隐式转换为A类对象类似这里是将A类对象aa3作为参数传递给B类的构造函数// 从而隐式地将aa3转换为B类对象并赋值给bB b aa3;// 5. 创建一个指向B类对象b的常引用rbconst B rb aa3;return 0; } static成员 一、静态成员的概念 声明为static的类成员被称为类的静态成员。其中用static修饰的成员变量就是静态成员变量而用static修饰的成员函数则被叫做静态成员函数。需要特别注意的是静态成员变量一定要在类外进行初始化哦。 二、静态成员的特点 1.共享性 静态成员变量为所有类对象所共享不属于某个具体的对象不存在对象中存放在静态区。 这意味着什么呢我们来看下面这段代码 #include iostream using namespace std; class Test { private:static int _n; }; int main() {cout sizeof(Test) endl;return 0; } 在这个例子中计算Test类的大小结果为 1。这是因为静态成员_n是存储在静态区的它属于整个类也属于类的所有对象。所以在计算类的大小或是类对象的大小时静态成员并不计入其总大小之和呢。 2.必须在类外定义 静态成员变量必须在类外定义而且定义时不添加static关键字。例如 class A{public:private:// 类⾥⾯声明 static int _scount;};// 类外⾯初始化 int A::_scount 0; 注意这里静态成员变量_scount虽然是私有但是我们在类外突破类域直接对其进行了访问。这是一个特例不受访问限定符的限制否则就没办法对静态成员变量进行定义和初始化了。 3.静态成员函数没有隐藏的this指针 ⽤static修饰的成员函数称之为静态成员函数静态成员函数没有this指针。静态成员函数中可以访问其他的静态成员但是不能访问⾮静态的因为没有this指针。 注意⾮静态的成员函数可以访问任意的静态成员变量和静态成员函数。  class Test { public:static void Fun(){cout _a endl; //error不能访问非静态成员cout _n endl; //correct} private:int _a; //非静态成员static int _n; //静态成员 }; 这里的静态成员函数Fun试图访问非静态成员_a就会报错因为它没有this指针来指向具体的对象呀。不过它是可以访问静态成员_n的哦。 小贴士通常含有静态成员变量的类一般会含有一个静态成员函数专门用于访问静态成员变量呢。 三、访问静态成员变量的方法 突破类域就可以访问静态成员可以通过类名::静态成员或者对象.静态成员来访问静态成员变量 和静态成员函数。 1.公有静态成员变量的访问方式 当静态成员变量为公有时有以下几种访问方式哦 class Test { public:static int _n; //公有 }; // 静态成员变量的定义初始化 int Test::_n 0; int main() {Test test;cout test._n endl; //1.通过类对象突破类域进行访问cout Test()._n endl; //3.通过匿名对象突破类域进行访问cout Test::_n endl; //2.通过类名突破类域进行访问return 0; } 我们既可以通过类对象、匿名对象来突破类域访问公有静态成员变量也可以直接通过类名来访问呢。匿名对象下面有讲 2.私有静态成员变量的访问方式  当静态成员变量为私有时情况就不太一样啦得通过成员函数来访问哦 #include iostream using namespace std; class Test { public:static int GetN(){return _n;} private:static int _n; }; // 静态成员变量的定义初始化 int Test::_n 0; int main() {Test test;cout test.GetN() endl; //1.通过对象调用成员函数进行访问cout Test().GetN() endl; //2.通过匿名对象调用成员函数进行访问cout Test::GetN() endl; //3.通过类名调用静态成员函数进行访问return 0; } 在这里我们通过定义一个获取静态成员变量值的静态成员函数GetN然后利用对象、匿名对象或者直接通过类名调用这个函数来间接访问私有静态成员变量哦。 3.静态成员的访问级别 静态成员也是类的成员受public、protected、private访问限定符的限制。 所以当静态成员变量设置为private时就算我们突破了类域也不能直接对其进行访问呢得按照上面说的私有静态成员变量的访问方式来操作哦。 两个问题 一静态成员函数能否调用非静态成员函数 答案是不可以哦。因为非静态成员函数的第一个形参默认为this指针而静态成员函数中没有this指针呀所以静态成员函数没办法调用非静态成员函数呢。 二非静态成员函数能否调用静态成员函数 这个是可以的哦。因为静态成员函数和非静态成员函数都在类中在类里面是不受访问限定符的限制的所以非静态成员函数可以自由地调用静态成员函数啦。 4、静态成员变量的特殊性 静态成员变量不能在声明位置给缺省值初始化因为缺省值是个构造函数初始化列表的静态成员 变量不属于某个对象不⾛构造函数初始化列表。 非静态量申明赋缺省值 class Class { public:Class() {} private:int memberVariable 10; // 这里给成员变量声明了缺省值 }; 静态成员变量属整个类存于静态存储区且被所有对象共享不依赖具体对象创建不走构造函数初始化列表所以不能像普通成员变量那样在声明时赋缺省值初始化需在类外单独按“类名::静态成员变量名 值;”方式初始化 class AnotherClass { public:AnotherClass() {} private:static int staticMemberVariable; }; // 必须在类外进行初始化如下所示 int AnotherClass::staticMemberVariable 20; 总结 由于静态成员变量的这种特殊性质 —— 属于整个类且不与具体对象的创建绑定导致它不能使用针对具体对象的构造函数初始化列表那种方式来在声明位置给缺省值进行初始化。 5、两道面试题 求123...n  牛客网 https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId13tqId11200tPage3rp3ru/ta/coding-interviewsqru/ta/coding-interviews/question-ranking // 定义Sum类 class Sum { public:// Sum类的默认构造函数Sum(){// 每次创建Sum类的对象时将当前的_i值累加到_ret中_ret _i;// 然后将_i的值自增1以便下次累加时使用更新后的_i值_i;}// 静态成员函数用于获取静态成员变量_ret的值static int GetRet(){return _ret;} private:// 声明静态成员变量_i用于在每次构造Sum类对象时进行累加操作初始值将在类外定义static int _i;// 声明静态成员变量_ret用于存储累加的结果初始值将在类外定义static int _ret; };// 在类外对Sum类的静态成员变量_i进行初始化初始值设为1 int Sum::_i 1; // 在类外对Sum类的静态成员变量_ret进行初始化初始值设为0 int Sum::_ret 0;// 定义Solution类 class Solution { public:// 函数Sum_Solution用于根据传入的参数n计算累加结果并返回int Sum_Solution(int n) {// 创建一个变长数组arr数组元素类型为Sum类数组大小为n// 这里创建Sum类对象数组的过程会多次调用Sum类的默认构造函数从而实现累加操作Sum arr[n];// 返回Sum类中存储的累加结果通过调用Sum类的静态成员函数GetRet来获取return Sum::GetRet();} }; 这段代码的主要功能是通过创建Sum类的对象数组来实现累加操作最后通过Sum类的静态成员函数获取累加的结果。其中利用了静态成员变量在类的多个对象间共享数据的特性以及静态成员函数可以直接通过类名调用的特点。 设已经有A,B,C,D4个类的定义程序中A,B,C,D构造函数调⽤顺序为 设已经有A,B,C,D4个类的定义程序中A,B,C,D析构函数调⽤顺序为 A D B A CB B A D CC C D B AD A B D CE C A B DF C D A B C c;int main(){A a;B b;static D d;}return 0构造函数调用顺序思路 全局对象C c;构造函数在main函数前调用。main函数里局部对象按定义顺序调用构造函数先A a;再B b;。静态局部对象static D d;构造函数在全局及同函数非静态局部对象构造完后首次执行到其定义语句时调用。顺序为C A B D选E。 析构函数调用顺序思路 main函数结束时局部对象按定义逆序析构先B b;再A a;。静态局部对象static D d;析构函数在同函数非静态局部对象析构完后程序结束时调用。全局对象C c;析构函数最后在程序完全结束时调用。顺序为B A D C。 友元 友元提供了⼀种突破类访问限定符封装的⽅式友元分为友元函数和友元类在函数声明或者类 声明的前⾯加friend并且把友元声明放到⼀个类的⾥⾯。 一、友元函数 1.友元函数的基本特性 友元函数可以直接访问类的私有成员这一点打破了类的封装限制为我们在某些特定场景下提供了便利。 但需要注意的是它是定义在类外部的普通函数并不属于任何类哦。不过呢要想让它具备访问类私有成员的 “特权”就需要在类的内部进行声明并且声明时要加上friend关键字。 2.友元函数在运算符重载中的应用 以日期类为例重载operator和operator时 若将operator重载为成员函数cout与隐含的this指针会争占首参位置this默认为首参但cout需为首参才好用所以只能重载为全局函数可这又导致类外无法访问日期类成员此时友元函数就有用了operator同理。C 的和能自动识别类型是因库中已对内置类型重载自定义日期类要实现此功能得写对应重载函数像这样在Date类内声明operator和operator为友元函数之后的重载函数就能访问Date类私有成员实现正确输入输出操作。 class Date {// 友元函数的声明friend ostream operator(ostream out, const Date d);friend istream operator(istream in, Date d); public:Date(int year 0, int month 1, int day 1){_year year;_month month;_day day;} private:int _year;int _month;int _day; };// 运算符重载 ostream operator(ostream out, const Date d) {out d._year - d._month - d._day endl;return out; }// 运算符重载 istream operator(istream in, Date d) {in d._year d._month d._day;return in; } 在这个例子中通过在Date类内部声明operator和operator为友元函数它们就可以顺利访问Date类的私有成员变量_year、_month和_day从而实现了对日期类对象的正确输入输出操作。 3.友元函数的其他注意事项 友元函数虽然可以访问类的私有和保护成员但它可不是类的成员函数哦这一点要牢记呢。友元函数不能用const修饰这是因为它本身并不属于类的成员函数体系不存在通过const来限定对象状态不变的情况。友元函数可以在类定义的任何地方声明完全不受访问限定符的限制这为我们在编写代码时提供了一定的灵活性。一个函数还可以是多个类的友元函数呢比如我们可以有一个通用的函数根据不同的类对象进行不同的操作并且都能访问它们各自的私有成员。友元函数的调用与普通函数的调用原理相同都是通过函数名加上合适的参数来进行调用的。 二、友元类 1.友元类的基本特性 友元类中的成员函数都可以是另⼀个类的友元函数都可以访问另⼀个类中的私有和保护成员。 我们来看一个简单的例子 class A {// 声明B是A的友元类friend class B; public:A(int n 0):_n(n){} private:int _n; };class B { public:void Test(A a){// B类可以直接访问A类中的私有成员变量cout a._n endl;} }; 在这个例子中因为B是A的友元类所以B类中的Test函数就可以直接访问A类的私有成员变量_n啦。 2.友元类的关系特性 友元关系是单向的不具有交换性。就像上面的例子中B是A的友元所以在B类中可以直接访问A类的私有成员变量但是在A类中可不能访问B类中的私有成员变量呢。这一点在使用友元类时要特别注意不要误以为友元关系是双向的哦。友元关系不能传递。也就是说如果A是B的友元B是C的友元我们可不能就此推出A是C的友元呀。这种非传递性也限制了友元关系的扩散范围使得我们在设计类之间的友元关系时需要更加谨慎地考虑其必要性和影响范围。 三、友元机制的利弊权衡 友元机制的优势 特定场景下便利处理需频繁访问他类私有成员的复杂逻辑时友元函数或类可直取数据免复杂接口或间接访问使代码简洁、编程效率提高。 友元机制的劣势 增加类间耦合度友元关系下类或函数联系紧密一类变可能影响相关友元增代码出错风险。一定程度破坏封装性私有成员不再绝对 “私有”与封装原则相悖。 总之实际编程要谨慎用友元权衡利弊后确需突破封装实现特定功能时才用。 内部类 一、内部类的概念 如果一个类定义在另一个类的内部那么这个类就被称作内部类啦。 这里有几个需要特别注意的点哦 1.独立性 虽说它在另一个类的内部但此时的内部类可是一个独立的类哦它并不属于外部类呢。而且要记住不能通过外部类的对象去调用内部类哦它们在本质上还是相互独立的个体只是存在着特殊的关联关系。 2.访问权限关系 外部类对内部类并没有任何优越的访问权限哦它们各自有着自己的访问规则和限制。不过呢内部类是外部类的友元类哦这意味着内部类可以通过外部类的对象参数来访问外部类中的所有成员呢。但反过来外部类可不是内部类的友元哦这种友元关系是单向的呢。 二、内部类的特性 1.定义区域灵活性 内部类可以定义在外部类的public、private以及protected这三个区域中的任一区域哦。这就给我们在设计代码结构时提供了很大的灵活性我们可以根据具体的需求和想要实现的封装程度来选择合适的区域放置内部类呢。 2.访问外部类成员的便利性 内部类有着很方便的访问外部类成员的能力哦。它可以直接访问外部类中的static、枚举成员而且不需要借助外部类的对象或者类名呢。这使得内部类在处理与外部类相关的业务逻辑时能够更加顺畅地获取所需的信息啦。 3.大小独立性 还有一个很重要的特性就是外部类的大小与内部类的大小是无关的哦。就像下面这个例子 #include iostream using namespace std; class A //外部类 { public:class B //内部类{private:int _b;}; private:int _a; }; int main() {cout sizeof(A) endl; //外部类的大小return 0; } 在这个例子中外部类A的大小为 4这里假设int类型占 4 个字节它仅仅取决于自身的成员变量_a和内部类B的大小没有任何关系哦。 三、用内部类优化上面的面试题 class Solution {class Sum{public:// Sum类的默认构造函数// 每次创建Sum类的对象时会执行以下操作Sum(){// 将当前的_i值累加到_ret中实现累加功能_ret _i;// 将_i的值自增1以便下次创建Sum类对象时进行新的累加操作_i;}};// 声明静态成员变量_i用于在Sum类的构造函数中进行累加操作的计数等相关用途// 其初始值将在类外进行初始化static int _i;// 声明静态成员变量_ret用于存储累加的结果// 其初始值将在类外进行初始化static int _ret;public:// 函数Sum_Solution用于根据传入的参数n计算累加结果并返回int Sum_Solution(int n) {// 创建一个变长数组arr数组元素类型为内部类Sum// 这里通过创建Sum类对象数组的方式多次调用Sum类的默认构造函数来实现累加操作// 数组大小为n即会创建n个Sum类的对象每个对象的构造函数都会对_i和_ret进行相应操作Sum arr[n];// 返回累加的最终结果即静态成员变量_ret的值return _ret;} };// 在类外对Solution类的静态成员变量_i进行初始化初始值设为1 int Solution::_i 1; // 在类外对Solution类的静态成员变量_ret进行初始化初始值设为0 int Solution::_ret 0; 匿名对象 一、匿名对象的生命周期概念 ⽤类型(实参)定义出来的对象叫做匿名对象相⽐之前我们定义的类型对象名(实参)定义出来的叫有名对象 int main() {// 这就是匿名对象的定义啦没有给它取名字哦A(1);A();return 0; } 匿名对象就是这样直接在创建的地方使用不需要像有名对象那样有一个特定的名字来指代它。 二、匿名对象的生命周期 匿名对象⽣命周期只在当前⼀⾏⼀般临时定义⼀个对象当前⽤⼀下即可就可以定义匿名对象。 比如说我们在 main 函数里定义了匿名对象 A(); 或者 A(1);当这一行代码运行结束之后紧接着下一行就会看到输出 ~A() 如果自己在析构函数里写了打印函数的话这就说明它的析构函数已经被调用啦它的使命也就完成咯。 而且要注意哦即使匿名对象参数无参的时候我们也是需要将括号带着的呢比如 A() 这样的写法才是正确的哦。 三、有名对象与匿名对象调用类中函数的区别 1.有名对象调用函数 int main() {// 先创建有名对象s1Solution s1;// 通过有名对象s1调用Sum_Solution函数cout s1.Sum_Solution(10) endl;return 0; } 2.匿名对象调用函数 int main() {// 创建匿名对象Solution()的同时调用Sum_Solution函数cout Solution().Sum_Solution(10) endl;return 0; } 四、匿名对象在其他方面的特性及注意事项 1.匿名对象在函数缺省值设置中的应用 在给函数进行缺省值的设置的时候我们是可以用到匿名对象的哦。比如在函数定义中可以这样写 void func(A aa A(1)) {} 这里就利用了匿名对象 A(1) 来给函数参数 aa 设置了缺省值呢。 2.匿名对象的引用操作 匿名对象也是可以进行引用操作的哦不过要注意啦因为匿名对象是具有常性的所以我们需要在左边添加 const 哦。就像这样 int main() {const A r A();return 0; } 当匿名对象被引用之后它的生命周期是会被延长的哦此时这个匿名对象的生命周期就跟着引用走啦是伴随着引用的销毁而销毁的呢。如果这个匿名对象销毁之后但是引用还在那这个引用就变成了野引用了这可是要特别注意避免的情况哦。 匿名对象的特点 没有名称匿名对象没有明确的变量名它们直接在创建的地方使用。 短生命周期通常它们的生命周期很短通常仅限于创建它们的上下文。 简化代码匿名对象有助于避免为临时对象命名减少不必要的代码。 五、匿名对象的常见使用场景及优点 常见使用场景 方法参数在传递对象作为方法参数时直接创建匿名对象而不需要事先定义它哦。就像在其他编程语言中的这个例子一样 public class Demo {public void printData(Person person) {System.out.println(person.getName());}public static void main(String[] args) {Demo demo new Demo();demo.printData(new Person(Jean, 30)); // 直接创建匿名对象} } 短暂的计算在需要临时对象来执行某些逻辑时使用匿名对象是很方便的哦。比如在 C 里可以这样 int result new Calculator().add(10, 20); // 创建匿名Calculator对象执行加法 初始化列表在某些语言中匿名对象还用于在构造函数中初始化对象或集合呢比如在 JavaScript 中的类似用法。 优点 简洁性匿名对象最大的优点之一就是能避免为短暂的对象创建冗余变量从而简化代码哦。让代码看起来更加简洁明了尤其是在一些只需要临时使用对象的场景下。更好的性能在某些情况下匿名对象可能避免了不必要的对象引用这样有助于垃圾回收更快地回收内存呢从而在一定程度上提升了程序的性能。 六、匿名对象的注意事项 虽然匿名对象能简化代码但也可能让代码变得难以调试或维护哦因为没有明确的对象引用嘛。所以如果多个地方需要使用相同的对象建议还是使用具名对象哦这样在调试和维护代码的时候会更加方便清晰呢。 对象拷⻉时的编译器优化 一、编译器优化的背景与原则 现代编译器始终秉持着一个重要目标在确保程序正确性不受影响的前提下竭尽全力提高程序的运行效率。在对象拷贝相关的操作中比如传参和传返回值过程编译器会想尽办法减少那些可有可无的拷贝操作。毕竟每一次不必要的拷贝都可能消耗额外的时间和内存资源而通过优化就能让程序在运行过程中更加轻盈高效。 值得注意的是关于具体该如何进行这些优化C 标准并没有给出严格的规定哦。这就好比给了各个编译器充分的自由发挥空间它们会依据自身的情况和算法来制定相应的优化策略。目前主流的相对新一点的编译器以及一些更为 “激进” 的编译器都有着各自独特的优化手段呢。   二、常见的编译器优化策略及示例 1.连续拷贝的合并优化 许多编译器对于连续一个表达式步骤中的连续拷贝操作会进行合并优化就像我们常见的以下几种情况 首先来看一个简单的类 A 的定义 #includeiostream using namespace std;class A { public:A(int a 0): _a1(a){cout A(int a) endl;}A(const A aa): _a1(aa._a1){cout A(const A aa) endl;}A operator(const A aa){cout A operator(const A aa) endl;if (this! aa){_a1 aa._a1;}return *this;}~A(){cout ~A() endl;} private:int _a1 1; }; 2.返回值优化RVO 当函数返回一个对象时编译器可以施展一种很厉害的优化手段 —— 返回值优化RVO。具体来说编译器可以直接在调用方的内存空间中构造返回的对象而不是依照常规先在函数内部构造好然后再拷贝到调用方。这样一来就能够巧妙地避免一次不必要的对象拷贝啦 举个例子  MyClass createObject() {return MyClass(); } 在没有开启返回值优化RVO时MyClass() 会先在函数内部创建然后再拷贝到调用方。而一旦开启了 RVOMyClass 就可以直接在调用方的内存空间中构造大大节省了拷贝的开销呢。 3.移动语义C11 及之后版本 在 C11 及之后的版本中引入了移动语义这一强大的优化利器。当对象被移动时编译器会通过一种 “偷取” 资源的方式来避免深拷贝哦。移动构造函数和移动赋值运算符赋予了编译器从源对象中 “偷取” 资源的能力而不是傻乎乎地去复制它们。这种优化在处理临时对象或者对象需要从另一个作用域转移的情况时显得尤为有用呢。 MyClass obj1; MyClass obj2 std::move(obj1); // 通过移动语义“偷取”资源而不是拷贝 这里通过 std::move 函数触发了移动语义使得 obj2 可以直接获取 obj1 的资源而无需进行繁琐的拷贝操作。 4.拷贝省略C17 引入 C17 版本引入了强制的拷贝省略规则哦。这意味着在某些特定情况下即使没有定义移动构造函数或移动赋值运算符编译器也会跳过拷贝操作直接构造目标对象呢。 就像下面这个例子 MyClass createObject() {MyClass obj;return obj; // 编译器可以省略拷贝或移动 } 5.内联优化Inline Expansion 对于那些小巧且频繁被调用的函数编译器可能会选择内联展开函数代码哦。这样做不仅能够减少函数调用所带来的开销而且还能让编译器在内联的上下文中进行更多的对象优化呢。 inline MyClass createObject() {return MyClass(); } 通过将函数内联展开编译器在处理这个函数返回对象的过程中就有更多机会施展它的优化魔法啦。 6.对象合并与内存重用 编译器还会进行一种对象合并的优化操作哦。它会仔细分析多个对象的生命周期如果发现它们不会同时存在于内存中那么编译器就会聪明地将它们分配在同一块内存空间中从而有效减少内存的占用呢。 7.懒惰拷贝写时拷贝COW 还有一种有趣的优化策略叫做懒惰拷贝也称为写时拷贝Copy on WriteCOW。当多个对象引用同一资源时编译器会选择延迟执行真正的拷贝操作直到有一个对象尝试修改该资源时才会进行拷贝。这样就能巧妙地避免不必要的深拷贝啦节省了大量的内存和时间开销呢。 8.循环展开与向量化优化 在对象拷贝的循环操作中编译器也不会闲着哦。它可能会进行循环展开和向量化优化将循环中的多个对象拷贝操作合并或者并行化处理以此来提高程序的性能呢。
文章转载自:
http://www.morning.cmzcp.cn.gov.cn.cmzcp.cn
http://www.morning.fmkjx.cn.gov.cn.fmkjx.cn
http://www.morning.ldynr.cn.gov.cn.ldynr.cn
http://www.morning.qrlsy.cn.gov.cn.qrlsy.cn
http://www.morning.nspbj.cn.gov.cn.nspbj.cn
http://www.morning.fkmqg.cn.gov.cn.fkmqg.cn
http://www.morning.mzbyl.cn.gov.cn.mzbyl.cn
http://www.morning.nyqnk.cn.gov.cn.nyqnk.cn
http://www.morning.srsln.cn.gov.cn.srsln.cn
http://www.morning.kgxrq.cn.gov.cn.kgxrq.cn
http://www.morning.sxwfx.cn.gov.cn.sxwfx.cn
http://www.morning.snnb.cn.gov.cn.snnb.cn
http://www.morning.fysdt.cn.gov.cn.fysdt.cn
http://www.morning.nmfwm.cn.gov.cn.nmfwm.cn
http://www.morning.kbdjn.cn.gov.cn.kbdjn.cn
http://www.morning.lqchz.cn.gov.cn.lqchz.cn
http://www.morning.rhph.cn.gov.cn.rhph.cn
http://www.morning.ftgwj.cn.gov.cn.ftgwj.cn
http://www.morning.xkwrb.cn.gov.cn.xkwrb.cn
http://www.morning.sbyhj.cn.gov.cn.sbyhj.cn
http://www.morning.rdgb.cn.gov.cn.rdgb.cn
http://www.morning.ppqzb.cn.gov.cn.ppqzb.cn
http://www.morning.sgfnx.cn.gov.cn.sgfnx.cn
http://www.morning.sqlh.cn.gov.cn.sqlh.cn
http://www.morning.tkrpt.cn.gov.cn.tkrpt.cn
http://www.morning.lfcfn.cn.gov.cn.lfcfn.cn
http://www.morning.jgncd.cn.gov.cn.jgncd.cn
http://www.morning.fpqsd.cn.gov.cn.fpqsd.cn
http://www.morning.nrlsg.cn.gov.cn.nrlsg.cn
http://www.morning.pjwrl.cn.gov.cn.pjwrl.cn
http://www.morning.thxfn.cn.gov.cn.thxfn.cn
http://www.morning.bxch.cn.gov.cn.bxch.cn
http://www.morning.kgcss.cn.gov.cn.kgcss.cn
http://www.morning.plqqn.cn.gov.cn.plqqn.cn
http://www.morning.hbxnb.cn.gov.cn.hbxnb.cn
http://www.morning.zfgh.cn.gov.cn.zfgh.cn
http://www.morning.xhgcr.cn.gov.cn.xhgcr.cn
http://www.morning.jyyw.cn.gov.cn.jyyw.cn
http://www.morning.kztts.cn.gov.cn.kztts.cn
http://www.morning.dhbyj.cn.gov.cn.dhbyj.cn
http://www.morning.tlpgp.cn.gov.cn.tlpgp.cn
http://www.morning.mfmx.cn.gov.cn.mfmx.cn
http://www.morning.hfxks.cn.gov.cn.hfxks.cn
http://www.morning.gxeqedd.cn.gov.cn.gxeqedd.cn
http://www.morning.qbdsx.cn.gov.cn.qbdsx.cn
http://www.morning.brwei.com.gov.cn.brwei.com
http://www.morning.jjzbx.cn.gov.cn.jjzbx.cn
http://www.morning.ctfwl.cn.gov.cn.ctfwl.cn
http://www.morning.xbtlt.cn.gov.cn.xbtlt.cn
http://www.morning.jftl.cn.gov.cn.jftl.cn
http://www.morning.bgpch.cn.gov.cn.bgpch.cn
http://www.morning.myzfz.com.gov.cn.myzfz.com
http://www.morning.srmpc.cn.gov.cn.srmpc.cn
http://www.morning.hjwxm.cn.gov.cn.hjwxm.cn
http://www.morning.tpnch.cn.gov.cn.tpnch.cn
http://www.morning.wwthz.cn.gov.cn.wwthz.cn
http://www.morning.cttgj.cn.gov.cn.cttgj.cn
http://www.morning.cwgfq.cn.gov.cn.cwgfq.cn
http://www.morning.ffbl.cn.gov.cn.ffbl.cn
http://www.morning.bkgfp.cn.gov.cn.bkgfp.cn
http://www.morning.tqpnf.cn.gov.cn.tqpnf.cn
http://www.morning.rynq.cn.gov.cn.rynq.cn
http://www.morning.kmqms.cn.gov.cn.kmqms.cn
http://www.morning.mcwrg.cn.gov.cn.mcwrg.cn
http://www.morning.hilmwmu.cn.gov.cn.hilmwmu.cn
http://www.morning.tbqbd.cn.gov.cn.tbqbd.cn
http://www.morning.tlfyb.cn.gov.cn.tlfyb.cn
http://www.morning.ykgp.cn.gov.cn.ykgp.cn
http://www.morning.dfbeer.com.gov.cn.dfbeer.com
http://www.morning.qbwmz.cn.gov.cn.qbwmz.cn
http://www.morning.fqssx.cn.gov.cn.fqssx.cn
http://www.morning.knnc.cn.gov.cn.knnc.cn
http://www.morning.mcjxq.cn.gov.cn.mcjxq.cn
http://www.morning.rckdq.cn.gov.cn.rckdq.cn
http://www.morning.lrzst.cn.gov.cn.lrzst.cn
http://www.morning.xrmwc.cn.gov.cn.xrmwc.cn
http://www.morning.lqytk.cn.gov.cn.lqytk.cn
http://www.morning.cjnfb.cn.gov.cn.cjnfb.cn
http://www.morning.hjlwt.cn.gov.cn.hjlwt.cn
http://www.morning.drwpn.cn.gov.cn.drwpn.cn
http://www.tj-hxxt.cn/news/262203.html

相关文章:

  • 网站设计公司无锡做a小视频网站
  • 昆明网页建站模板dede网站怎么备份
  • 宁波网站设计首选荣盛网络嘉兴做微网站设计
  • 用KEGG网站做通路富集分析微信官网网站模板
  • 网页模板网站生成物流网站大全
  • 合肥做网站一般多少钱网站建设全包一条龙
  • 当雄网站建设网站域名备案变更
  • 化工网站模板下载一 网站开发体会
  • 怎样设置 自己的网站遵义信息港
  • 中华保险网站做网站用什么数据库好用
  • 电脑网站搜索如何做网站 河北 备案 慢
  • 关系的网站昆明优化公司
  • 济宁网站建设找哪家兰州网站建设q479185700惠
  • 南通seo网站建设费用贵阳网站定制开发
  • 网站开发费用国家标准上海做推广的公司
  • 佛山市做网站国外建站数据
  • 重庆市建设公共资源交易中心网站河南高端网站高端网站建设
  • 藤虎网络广州网站建设奇艺广州网站建设熊掌号
  • 石家庄市新华区建设局网站环球网最新新闻
  • asp.net 窗体网站金华住房和城乡建设厅网站
  • 有域名自己怎么做网站网络广告策划书
  • 引擎网站推广法石家庄尚武科技
  • 网站建设及运维合同什么网站 是cms系统下载地址
  • 高要seo整站优化人工智能网页设计
  • 北京网站建设报价明细网店装修教程免费
  • 有哪些做废品的网站平台门户建设
  • 手游传奇发布网站999做一个购物网页
  • 网站开发目的简介网站运营维护合同
  • 做网站用的hu软件php开发的培训网站建设
  • 便捷网站建设推荐注册网站需要备案吗