【类和对象(下)】C++类与对象的进阶艺术:初始化列表到性能优化的完全指南

【类和对象(下)】C++类与对象的进阶艺术:初始化列表到性能优化的完全指南

🔥艾莉丝努力练剑:个人主页

专栏传送门:《C语言》《数据结构与算法》C/C++干货分享&学习过程记录Linux操作系统编程详解笔试/面试常见算法:从基础到进阶

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬艾莉丝的简介:


 C++的两个参考文档:

老朋友(非官方文档):cplusplus

官方文档(同步更新):cppreference


目录

一、只缘构造又重来

1.1  再探构造:初始化列表

1.2  初始化列表知识点总结

1.3  成员变量走初始化列表的逻辑

1.4  例题

1.5  成员变量

1.5.1  每个成员变量只能出现一次

1.5.2  成员变量引用

1.6  显示、缺省初始化

1.6.1  显示、缺省

1.6.2  显示、缺省混用的场景

1.6.3  代码演示

1.7  自定义类型/内置类型

1.7.1  自定义类型

1.7.2  内置类型

1.8  博主手记

二、类型转换

2.1  类型转换的概念:explicit

2.2  代码展示

三、static成员

3.1  static成员的概念

3.2  【1+2+3+...+n】

3.2.1  题目描述

3.2.2  代码实现

3.3  题目

四、友元(friend)——“轻度社交”

4.1  友元的概念

4.2  友元重点总结

4.3  代码实现

4.4  前置声明

4.5  耦合性:什么样的设计才是好的软件设计

五、内部类——不是成员

5.1  内部类的知识点

5.2  代码演示

6.3  用内部类重新解决【1+2+3+...+n】问题

六、匿名对象和有名对象

6.1  知识点

6.2  代码演示

七、优化——对象拷贝时的编译器优化

7.1  知识点

7.1.1  概念

7.1.2  优化

7.2  优化展示(对比无优化)

7.3  NRVO && URVO

7.4  博主手记

八、本文代码

Test.cpp:

结尾


一、只缘构造又重来

1.1  再探构造:初始化列表

1、之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有一种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个”成员变量”后面跟一个放在括号中的初始值或表达式。

2、每个成员变量在初始化列表中只能出现一次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。

3、引用成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则会编译报错。

4、C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。

5、尽量使用初始化列表初始化,因为那些你不在初始化列表初始化的成员也会走初始化列表,如果这个成员在声明位置给了缺省值,初始化列表会用这个缺省值初始化。如果你没有给缺省值,对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。

6、初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持一致。

1.2  初始化列表知识点总结

(1)“ : ”——开始,分隔;

(2)无论是否显示写初始化列表,每个构造函数都有初始化列表;

(3)无论是否在初始化列表显示初始化成员变量,每个成员变量都要走初始化列表初始化;

(4)如下图所示——

1.3  成员变量走初始化列表的逻辑

1.4  例题

下面程序的运行结果是什么()

A. 输出 1  1 

B. 输出 2  2 

C. 编译报错 

D. 输出 1 随机值 

E. 输出 1  2 

F. 输出 2  1 

代码演示如下:

#include<iostream> using 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(); }

正确答案:D选项。

1.5  成员变量

1.5.1  每个成员变量只能出现一次

1.5.2  成员变量引用

成员变量引用——跟外面的变量联系起来。

1.6  显示、缺省初始化

1.6.1  显示、缺省

C++11支持在内置类型给缺省值:

显示传用显示,没有用缺省——

1.6.2  显示、缺省混用的场景

1.6.3  代码演示

代码演示如下——

#include<iostream> using namespace std; class Time { public: Time(int hour) :_hour(hour) { cout << "Time()" << endl; } private: int _hour; }; class Date { public: Date(int& x, int year = 1, int month = 1, int day = 1) :_year(100) {} //C++11支持在内置类型给缺省值 private: //声明,缺省值 int _year = 1; int _month = 1; int _day; //显示传用显示,没有用缺省 Time _t = 1; const int _x = 1; int* _ptr = (int*)malloc(40); }; int main() { Date d1;//没有合适的构造函数可用 return 0; }

1.7  自定义类型/内置类型

1.7.1  自定义类型

自定义类型(类类型)会去调用它的对应的默认构造。

1.7.2  内置类型

内置类型(编译器)会不做处理。

1.8  博主手记


二、类型转换

2.1  类型转换的概念:explicit

1、C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。

2、构造函数前面加explicit就不再支持隐式类型转换。

3、类类型的对象之间也可以隐式转换,需要相应的构造函数支持。

我们可以用Linux的XShell查看一下有没有优化——

2.2  代码展示

class A { public: //构造函数explicit就不再支持隐式类型转换 //explicit A(int a1) A(int a1) :_a1(a1) { } private: int _a1 = 1; int _a2 = 2; }; void func(const A& aa = 1) {} class Stack { public: void Push(const A& a) { } }; int main() { int i = 1; double d = i; const double& ref1 = i; //构造 A a1(1); //隐式类型转换 A a2 = 1; const A& ref2 = a1; const A& ref3 = 1; func(a1); func(1); func(); Stack st1; A a3(3); st1.Push(a3); st1.Push(3); return 0; } //总结:不修改成员变量的成员函数都应该加上const,这样const对象可以调用、普通对象也可以调用 class A { public: //构造函数explicit就不再支持隐式类型转换 //explicit A(int a1) A(int a1) :_a1(a1) { cout << "A(int a1)" << endl; } A(const A& aa) { cout << "A(const A&& aa)" << endl; } A(int a1, int a2) :_a1(a1) ,_a2(a2) { } int Get() const { return _a1 + _a2; } private: int _a1 = 1; int _a2 = 2; }; class B { public: B(const A& a) :_b(a.Get()) { } private: int _b = 0; }; int main() { //构造 A a1(1); //2为参数构造临时对象,临时对象拷贝构造a2 -> 优化为直接构造 A a2 = 2; const A& ref1 = 3; A a3(1, 1); A a4 = (1, 1); const A& ref2 = { 1,1 }; //Stack st1; //st1.Push(a4); //st1.Push({ 2,2 }); B b1 = a3; const B& ref3 = a3; return 0; }

三、static成员

3.1  static成员的概念

1、用static修饰的成员变量,称之为静态成员变量,静态成员变量一定要在类外进行初始化。

2、静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。

3、用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。

4、静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。

5、非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。

6、突破类域就可以访问静态成员,可以通过类名:静态成员或者对象.静态成员来访问静态成员变量和静态成员函数。

7、静态成员也是类的成员,受public、protected、private访问限定符的限制。

8、静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表。

3.2  【1+2+3+...+n】

牛客网链接:求1+2+3+...+n

这个等我们接下来介绍完【内部类】的内容之后还会有一种写法,现在先用这个实现——

3.2.1  题目描述

3.2.2  代码实现

代码演示如下——

class Sum { public: Sum() { _ret += _i; ++_i; } static int GetRet() { return _ret; } private: static int _i; static int _ret; }; int Sum::_i = 1; int Sum::_ret = 0; class Solution { public: int Sum_Solution(int n) { Sum arr[n]; return Sum::GetRet(); } };

3.3  题目


四、友元(friend)——“轻度社交”

4.1  友元的概念

1、友元提供了一种突破类访问限定符封装的方式,友元分为:友元函数和友元类,在函数声明或者类声明的前面加friend,并且把友元声明放到一个类的里面。

2、外部友元函数可访问类的私有和保护成员,友元函数仅仅是一种声明,他不是类的成员函数。

3、友元函数可以在类定义的任何地方声明,不受类访问限定符限制。

4、一个函数可以是多个类的友元函数。

5、友元类中的成员函数都可以是另一个类的友元函数,都可以访问另一个类中的私有和保护成员。

6、友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。

7、友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。

8、有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

4.2  友元重点总结

1、友元(friend),突破访问限定符(封装的一种方式)的限制——可以访问私有 / 公有;

2、前置声明(如class B;):编译器界定是向上查找——这个设计是为了保证编译速度(设定原因),但这个前置声明只能保证编译器认识class B这个类型,至于里面的细节,编译器是不知道的,比如里面的成员_b1,要做声明和定义的分离;

3、友元是单向的,以社交软件打比方,友元就像是抖音(关注,只是轻度社交关系),而不像是双向关注(朋友圈)的微信(深度社交关系),那怎么办?看第四点——;

4、互相成为友元(谁想访问谁,谁就成为谁的友元)——

成员函数成为友元(要麻烦一点,要指明类域、...等);

5、友元关系不能传递:类似于西欧中世纪的附庸关系——“我附庸的附庸不是我的附庸”,——

4.3  代码实现

//前置声明,都则A的友元函数声明编译器声明编译器不认识B class B; class A { //友元声明 friend void func(const A& aa, const B& bb); private: int _a1 = 1; int _a2 = 2; }; class B { //友元声明 friend void func(const A& aa, const B& bb); private: int _a1 = 3; int _a2 = 4; }; void func1(const A& aa, const B& bb) { cout << aa._a1 << endl; cout << bb._b1 << endl; } //xxx.h class C { //友元声明 friend class D; friend func1(const D& dd); private: int _a1 = 1; int _a2 = 2; }; class D { friend class C; public: void func1(const C& aa); void func2(const C& aa); private: int _b1 = 3; int _b2 = 4; }; //xxx.cpp void C::func1(const D& dd) { cout << dd._b1 << endl; } void D::func1(const C& aa) { cout << aa._a1 << endl; cout << _b1 << endl; } void D::func2(const C& aa) { cout << aa._a2 << endl; cout << _b2 << endl; } int main() { A aa; B bb; func(aa, bb); C cc; D dd; dd.func1(cc); dd.func2(cc); return 0; }

4.4  前置声明

4.5  耦合性:什么样的设计才是好的软件设计

耦合性(关系太亲近了<-耦合性太高)增加、破坏了封装——友元不宜多。


五、内部类——不是成员

内部类不是成员,定义在类内部,也就是说:“内部类是独立的类”。

5.1  内部类的知识点

1、如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。

2、内部类默认是外部类的友元类。

3、内部类本质也是一种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方都用不了。

5.2  代码演示

class A { private: static int _k; int _h = 1; public: //内部类 class B//B默认就是A的友元 { public: void foo(const A& a) { cout << _k << endl;//OK cout << a._h << endl;//OK } private: int _b1; }; }; int main() { cout << sizeof(A) << endl; A::B bb; return 0; }

6.3  用内部类重新解决【1+2+3+...+n】问题

代码演示如下:

//内部类 class Solution { class Sum { public: Sum() { _ret += _i; ++_i; } }; public: int Sum_Solution(int n) { Sum arr[n]; return _ret; } private: static int _i; static int _ret; }; int Solution::_i = 1; int Solution::_ret = 0;

对比前一种写法——


六、匿名对象和有名对象

6.1  知识点

1、用类型(实参)定义出来的对象叫做匿名对象,相比之前我们定义的类型对象名(实参)定义出来的叫有名对象;

2、匿名对象生命周期只在当前一行,一般临时定义一个对象当前用一下即可,就可以定义匿名对象。

6.2  代码演示

class Solution { class Sum { public: Sum() { _ret += _i; ++_i; } }; public: void Clear() { _i = 1; _ret = 0; } int Sum_Solution(int n) { Sum* ptr = new Sum[n]; delete[] ptr; return _ret; } ~Solution() { cout << "~Solution()" << endl; } private: static int _i; static int _ret; }; int Solution::_i = 1; int Solution::_ret = 0; void Func(const Solution& s = Solution(), int i = 1) { } int main() { Solution s;//有名对象 cout << s.Sum_Solution(10) << endl; s.Clear(); //生命周期只在当前一行 Solution();//匿名对象 cout << Solution().Sum_Solution(10) << endl; //const 引用会延长匿名对象的生命周期,生命周期和const引用一样 const Solution& ref = Solution(); Func(Solution()); Func(s); Func(); return 0; }

注意:const 引用会延长匿名对象的生命周期,生命周期和const引用一样。

再来看一段代码,对匿名对象有更进一步的认识——

class A { public: A(int a = 0) :_a(a) { cout << "A(int a)" << endl; } ~A() { cout << "~A()" << endl; } private: int _a; }; class Solution { public: int Sum_Solution(int n) { //... return n; } }; int main() { A aa1; // 不能这么定义对象,因为编译器⽆法识别下⾯是⼀个函数声明,还是对象定义 //A aa1(); // 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字, // 但是他的⽣命周期只有这一行,我们可以看到下一行他就会自动调用析构函数 A(); A(1); A aa2(2); // 匿名对象在这样场景下就很好用,当然还有一些其他使用场景,这个我们以后遇到了再说 Solution().Sum_Solution(10); return 0; }

七、优化——对象拷贝时的编译器优化

7.1  知识点

7.1.1  概念

1、现代编译器会为了尽可能提高程序的效率,在不影响正确性的情况下会尽可能减少一些传参和传返回值的过程中可以省略的拷贝。

2、如何优化C++标准并没有严格规定,各个编译器会根据情况自行处理。当前主流的相对新一点的编译器对于连续一个表达式步骤中的连续拷贝会进行合并优化,有些更新更“激进”的编译器还会进行跨行跨表达式的合并优化。

3、linux下可以将下面代码拷贝到test.cpp文件,编译时用

g++test.cpp-fno-elide-constructors

的方式关闭构造相关的优化。

7.1.2  优化

临时对象比较小,可以存在寄存器;优化就是省略掉之间的临时对象。

7.2  优化展示(对比无优化)

7.3  NRVO && URVO

C++官网下图对应位置的网址:Copy elision

连续一个表达式步骤中的连续拷贝会进行合并优化。

优化前后对比——

这里的原理就是:

原来要构造三个,现在只构造一个——只构造一个,因为这里的aa是aa1的别名。

编译器超前(极致优化),g++也是如此优化的。

优化前后对比——

跨行优化——编译器比文档更加(或者说超前)激进,直接跨行。

7.4  博主手记


八、本文代码

Test.cpp:

#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; //class Time //{ //public: // Time(int hour) // :_hour(hour) // { // cout << "Time()" << endl; // } //private: // int _hour; //}; // //class Date //{ //public: // Date(int& x, int year = 1, int month = 1, int day = 1) // :_year(year) // , _month(month) // , _day(day) // , _t(12) // ,_ref(x) // , _n(1) // { // //error C2512 :“Time”:没有合适的默认构造函数可以用 // //error C2530 :“Date::_ref” :必须初始化引用 // //error C2789 :“Date::_n” : 必须初始化常量限定类型的对象 // } // //private: // //声明 // int _year; // int _month; // int _day; // // int& _ref;//引用 // const int _n;//const // Time _t;//没有默认构造 //}; // //class Stack //{ //public: // Stack(int n) // { // cout << "Stack(int n)" << endl; // } //}; // //class MyQueue //{ //public: // MyQueue(int n = 4) // :_st1(n) // ,_st2(n) // { } //private: // Stack _st1; // Stack _st2; //}; // //int main() //{ // int y = 0; // // 对象整体定义 // Date d1(y); // // ++y; // // MyQueue q1; // // return 0; //} //#include<iostream> //using namespace std; // //class Time //{ //public: // Time(int hour) // :_hour(hour) // { // cout << "Time()" << endl; // } //private: // int _hour; //}; // //class Date //{ //public: // Date(int& x, int year = 1, int month = 1, int day = 1) // :_year(100) // {} // //C++11支持在内置类型给缺省值 //private: ////声明,缺省值 //int _year = 1; //int _month = 1; //int _day; ////显示传用显示,没有用缺省 // // Time _t = 1; // const int _x = 1; // int* _ptr = (int*)malloc(40); //}; // //int main() //{ // Date d1;//没有合适的构造函数可用 // // return 0; //} //class Stack //{ //public: // Stack(int n = 4) // { // cout << "Stack(int n)" << endl; // } //}; // //class MyQueue //{ //public: // int _size = 0; // Stack _st1; // Stack _st2; //}; // //class A //{ //public: // A(int n = 10) // :_a((int*)malloc(sizeof(int)* n)) // , _size(0) // { // //检查 // if (_a == nullptr) // { // cout << "malloc fail" << endl; // exit(-1); // } // // memset(_a, 0, sizeof(int) * n); // } // //private: // int* _a; // int _size; //}; // //int main() //{ // MyQueue q1; // A aa; // // return 0; //} //class A //{ //public: // //构造函数explicit就不再支持隐式类型转换 // //explicit A(int a1) // A(int a1) // :_a1(a1) // { } // //private: // int _a1 = 1; // int _a2 = 2; //}; // //void func(const A& aa = 1) //{} // //class Stack //{ //public: // void Push(const A& a) // { } //}; // //int main() //{ // int i = 1; // double d = i; // // const double& ref1 = i; // // //构造 // A a1(1); // // //隐式类型转换 // A a2 = 1; // // const A& ref2 = a1; // const A& ref3 = 1; // // func(a1); // func(1); // func(); // // Stack st1; // A a3(3); // st1.Push(a3); // // st1.Push(3); // // return 0; //} ////总结:不修改成员变量的成员函数都应该加上const,这样const对象可以调用、普通对象也可以调用 //class A //{ //public: // //构造函数explicit就不再支持隐式类型转换 // //explicit A(int a1) // A(int a1) // :_a1(a1) // { // cout << "A(int a1)" << endl; // } // A(const A& aa) // { // cout << "A(const A&& aa)" << endl; // } // // A(int a1, int a2) // :_a1(a1) // ,_a2(a2) // { } // // int Get() const // { // return _a1 + _a2; // } // //private: // int _a1 = 1; // int _a2 = 2; //}; // //class B //{ //public: // B(const A& a) // :_b(a.Get()) // { } //private: // int _b = 0; //}; // //int main() //{ // //构造 // A a1(1); // // //2为参数构造临时对象,临时对象拷贝构造a2 -> 优化为直接构造 // A a2 = 2; // // const A& ref1 = 3; // // A a3(1, 1); // A a4 = (1, 1); // const A& ref2 = { 1,1 }; // // //Stack st1; // //st1.Push(a4); // //st1.Push({ 2,2 }); // // B b1 = a3; // const B& ref3 = a3; // // return 0; //} ////静态成员函数没有this指针 //class A //{ //public: // A(int a = 0) // :_a1(a) // , _a2(a) // { // ++_count; // } // // A(const A& t) // { // ++_count; // } // // static int GetCount() // { // // _a1++; 不能访问非静态成员,没有this // return _count; // } //private: // int _a1 = 1; // int _a2 = 1; //public: // //声明 // static int _count; //}; // //int A::_count = 0; // //非静态可以直接访问静态和非静态 //int main() //{ // A aa1; // cout << sizeof(aa1) << endl; // A* ptr = nullptr; // // A aa2 = 1; // cout << ptr->_count << endl; // cout << aa1._count << endl; // cout << A::_count << endl; // // cout << A::GetCount() << endl; // cout << aa2.GetCount() << endl; // cout << ptr->GetCount() << endl; // // return 0; //} //class Sum //{ //public: // Sum() // { // _ret += _i; // ++_i; // } // // static int GetRet() // { // return _ret; // } // //private: // static int _i; // static int _ret; //}; // //int Sum::_i = 1; //int Sum::_ret = 0; // //class Solution //{ //public: // int Sum_Solution(int n) // { // //变长数组 // //Sum arr[n]; // Sum* arr = new Sum[n]; // delete[] arr; // //比C语言的malloc、calloc、realloc、free更超标的来了 // // return Sum::GetRet(); // } //}; // ////int main() ////{ //// Solution s; //// cout << s.Sum_Solution(10) << endl; //// //// return 0; ////} // //class A //{ //public: // //构造 // A() // { // cout << "A()" << endl; // } // //析构 // ~A() // { // cout << "~A()" << endl; // } //}; // //class B //{ //public: // //构造 // B() // { // cout << "B()" << endl; // } // //析构 // ~B() // { // cout << "~B()" << endl; // } //}; // //class C //{ //public: // //构造 // C() // { // cout << "C()" << endl; // } // //析构 // ~C() // { // cout << "~C()" << endl; // } //}; // //class D //{ //public: // //构造 // D() // { // cout << "D()" << endl; // } // //析构 // ~D() // { // cout << "~D()" << endl; // } //}; // //C c; //int main() //{ // A a; // B b; // static D d; // // return 0; //} //------------------------------------------------------------------------------------------------ ////前置声明,都则A的友元函数声明编译器声明编译器不认识B //class B; // //class A //{ // //友元声明 // friend void func(const A& aa, const B& bb); //private: // int _a1 = 1; // int _a2 = 2; //}; // //class B //{ // //友元声明 // friend void func(const A& aa, const B& bb); //private: // int _a1 = 3; // int _a2 = 4; //}; // //void func1(const A& aa, const B& bb) //{ // cout << aa._a1 << endl; // cout << bb._b1 << endl; //} // ////xxx.h //class C //{ // //友元声明 // friend class D; // friend func1(const D& dd); //private: // int _a1 = 1; // int _a2 = 2; //}; // //class D //{ // friend class C; //public: // void func1(const C& aa); // void func2(const C& aa); //private: // int _b1 = 3; // int _b2 = 4; //}; // ////xxx.cpp //void C::func1(const D& dd) //{ // cout << dd._b1 << endl; //} // //void D::func1(const C& aa) //{ // cout << aa._a1 << endl; // cout << _b1 << endl; //} // //void D::func2(const C& aa) //{ // cout << aa._a2 << endl; // cout << _b2 << endl; //} // //int main() //{ // A aa; // B bb; // func(aa, bb); // // C cc; // D dd; // dd.func1(cc); // dd.func2(cc); // // return 0; //} //class A //{ //private: // static int _k; // int _h = 1; //public: // //内部类 // class B//B默认就是A的友元 // { // public: // void foo(const A& a) // { // cout << _k << endl;//OK // cout << a._h << endl;//OK // } // private: // int _b1; // }; //}; // //int main() //{ // cout << sizeof(A) << endl; // // A::B bb; // // return 0; //} //class Solution //{ // class Sum // { // public: // Sum() // { // _ret += _i; // ++_i; // } // }; //public: // void Clear() // { // _i = 1; // _ret = 0; // } // // int Sum_Solution(int n) // { // Sum* ptr = new Sum[n]; // delete[] ptr; // // return _ret; // } // // ~Solution() // { // cout << "~Solution()" << endl; // } // //private: // static int _i; // static int _ret; //}; // //int Solution::_i = 1; //int Solution::_ret = 0; // //void Func(const Solution& s = Solution(), int i = 1) //{ // //} // //int main() //{ // Solution s;//有名对象 // cout << s.Sum_Solution(10) << endl; // s.Clear(); // // //生命周期只在当前一行 // Solution();//匿名对象 // cout << Solution().Sum_Solution(10) << endl; // //const 引用会延长匿名对象的生命周期,生命周期和const引用一样 // const Solution& ref = Solution(); // // Func(Solution()); // Func(s); // Func(); // // return 0; //} 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; }; void f1(A aa) { } //int main() //{ // //构造+拷贝构造 优化 -> 构造 // A aa1 = 1; // cout << "=================" << endl; // // f1(aa1); // cout << "=================" << endl; // // f1(1); // cout << "=================" << endl; // // f1(A(1)); // cout << "=================" << endl; // // return 0; //} //C++官网:http://en.cpprefrence.com/w/cpp/language/copy elision.html //A f2() //{ // //NOVO // A aa; // cout << &aa << endl; // return aa; // // //URVO // return A(1); //} // //int main() //{ // A aa1 = f2(); // cout << &aa1 << endl; // // return 0; //} A f2() { //NOVO A aa; cout << &aa << endl; return aa; //URVO return A(1); } int main() { //不推荐 A aa1; aa1 = f2(); cout << &aa1 << endl; //推荐 A aa2 = f2(); return 0; }

结尾

往期回顾:

【C/C++】类和对象(中):(二)类的默认成员函数——拷贝构造,赋值拷贝——赋值运算符重载

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡

૮₍ ˶ ˊ ᴥ ˋ˶₎ა


Read more

全网最全!Python、PyTorch、CUDA 与显卡版本对应关系速查表

全网最全!Python、PyTorch、CUDA 与显卡版本对应关系速查表

摘要:搞深度学习,最痛苦的不是写代码,而是配环境! “为什么我的 PyTorch 认不出显卡?” “新买的显卡装了旧版 CUDA 为什么报错?” 本文提供一份保姆级的版本对应关系速查表,涵盖从 RTX 50 系列 (Blackwell) 到经典老卡的软硬件兼容信息。建议收藏保存,每次配环境前查一下,能省下大量的排坑时间! 🗺️ 核心逻辑图解 在看表格前,先理清显卡架构的代际关系与 CUDA 版本的强绑定逻辑。 📊 一、PyTorch 版本对照表 (推荐) PyTorch 是目前兼容性最好的框架,只要 CUDA 驱动版本 足高,通常都能向下兼容。对于使用最新硬件(如 RTX 50 系)的用户,请务必使用 2.4 或更高版本。 PyTorch 版本Python 版本推荐 CUDA适用显卡建议2.

By Ne0inhk

Qwen3-4B开源优势:可定制化部署企业级AI应用

Qwen3-4B开源优势:可定制化部署企业级AI应用 1. 引言:为什么企业需要自己的AI模型服务? 如果你在技术团队工作,最近可能经常听到这样的讨论:“我们能不能用大模型做个智能客服?”“能不能让AI帮我们分析业务数据?”“能不能定制一个符合我们行业术语的问答助手?” 想法很多,但一落地就遇到难题:用公有云API,数据安全不放心;用闭源模型,定制化程度不够;自己从头训练,成本高到吓人。这就像想开一家特色餐厅,结果发现要么只能租用别人的厨房(公有云),要么只能买现成的预制菜(闭源模型),要么就得从种菜开始学起(自研训练)。 今天我要介绍的Qwen3-4B-Instruct-2507,就是那个让你既能拥有自己的厨房,又能根据口味调整配方的解决方案。这是一个40亿参数的开源语言模型,最新版本在指令遵循、逻辑推理、多语言支持等方面都有显著提升,更重要的是,它完全开源,支持私有化部署。 在接下来的内容里,我不会只讲技术参数有多厉害,而是会带你实际走一遍:怎么用vLLM快速部署这个模型服务,怎么用Chainlit搭建一个简洁的对话界面,怎么验证服务是否正常运行。这些都是企业落地AI应

By Ne0inhk
【Python 基础】第1章:基础语法完全指南(变量/数据类型/运算符/字符串/输入输出)

【Python 基础】第1章:基础语法完全指南(变量/数据类型/运算符/字符串/输入输出)

Python 基础知识点概览 * 前言 * 1. 前置知识点 * 1.1 注释 * 2. 字面量 * 2.1 字面量定义 * 2.2 字面量类型(常见基础数据类型) * 2.3 代码示例 * 3. 变量 * 3.1 变量定义 * 3.2 代码示例 * 4. 标识符 * 4.1 标识符定义 * 5. 数据类型 * 5.1 查看数据实际类型 * 5.2 代码示例 * 6. 字符串 * 6.1 字符串定义 * 6.2 字符串拼接 * 6.

By Ne0inhk
AutoGPT+Python:让AI智能体自动完成复杂任务的终极指南

AutoGPT+Python:让AI智能体自动完成复杂任务的终极指南

AutoGPT+Python:让AI智能体自动完成复杂任务的终极指南 引言:在人工智能迈向自主化的新阶段,AutoGPT作为基于大语言模型(LLM)的自主智能体代表,正掀起一场让AI自己思考、自主执行的技术革命。当它遇上Python的全栈生态与极致灵活性,开发者不再只是调用AI接口,而是能深度定制专属智能体——让AI听懂自然语言、拆解复杂目标、调用外部工具、联网检索信息、迭代优化结果,独立完成从市场调研、内容创作、代码开发到自动化运维的全流程任务。 本文从核心原理、本地部署、Python实战、插件扩展、生产优化五大维度,手把手带你从0到1搭建可落地、可监控、可进化的AI智能体系统,不管是AI爱好者、全栈开发者还是创业者,都能靠这份指南,掌握下一代人机协作的核心生产力。 一、先搞懂:AutoGPT到底是什么? 传统ChatGPT类模型是被动应答,你问一句它答一句,需要人工一步步引导;而AutoGPT是自主智能体,你只给它一个最终目标,它就能自己完成: * 任务拆解:把复杂目标拆成可执行子步骤 * 自主决策:判断下一步该做什么、调用什么工具 * 记忆管理:短期记忆存上下文

By Ne0inhk