全局静态变量、函数内静态变量、attribute ((destructor))析构顺序 构造析构顺序的不确定性
以及静态函数获取的单例。
C++ 标准规定:同一个编译单元(同一个 cpp 文件)内,静态/全局对象的析构顺序与构造顺序相反。
但不同编译单元(不同 cpp 文件/so)之间的析构顺序是未定义的。
局部 static(即函数内 static)对象的析构顺序与其定义顺序有关,但也只在同一编译单元内有保证。
若一个变量仅在单个文件中可见,则建议将这个变量声明为静态全局变量,static修饰的静态全局变量仅在当前文件中可见 。
如果一个全局变量只被单个函数使用,将其改为该函数的静态局部变量可以进一步限制变量的作用域,提高代码的内聚性,降低耦合度。静态局部变量具有全局寿命但局部作用域 的特点,
静态全局变量是存储在**静态数据区的,**而不是栈区,因此静态全局变量的大小不会导致栈溢出。栈溢出通常是由于函数调用层次过深或局部变量过大导致的。
类的内存占用
1.32位系统中虚函数指针为4字节,64位为8字节
2.只需要考虑虚函数指针,虚函数表不计入某个类的资源
3.char占一字节,但是需要考虑内存调用
4.如果有虚继承,则多一个虚基类指针。
5.空类占一个字节(用于标识)
指针好题
int arr[5]={1,2,3,4,5};在这个数组的定义中,通常的理解arr 是数组的地址即数组首元素 的地址,进一步理解arr是一个int型的指针常量,常量+1地址偏移sizeof(int),所以arr+1是首元素下一个元素的地址;考虑到这一层就不难理解**&arr*的含义,&arr是对arr取地址,结果也是个地址,只是这个地址的类型是指向有5个int类型数据的数组的指针常量,这个常量+1地址偏移5 sizeof(int)。
各级指针算各级的 : 主要就是理解& 和 * 的“升级降级”;
链接:https://www.nowcoder.com/exam/test/89156461/submission?examPageSource=Intelligent&pid=62380309&testCallback=https%3A%2F%2Fwww.nowcoder.com%2Fexam%2Fintelligent%3FquestionJobId%3D10%26subTabName%3Dintelligent_page%26tagId%3D21000&testclass=%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91
Copy and Swap 传统做法operator=
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class MyString {private : char * data; public : MyString& operator =(const MyString& other) { if (this == &other) { return *this ; } delete [] data; data = new char [std::strlen (other.data) + 1 ]; std::strcpy (data, other.data); return *this ; } };
Copy and Swap
1 2 3 4 5 6 7 8 9 MyString& operator =(const MyString& other) { if (this == &other) { return *this ; } MyString tmp{other}; std::swap (data,other.data); return *this ; }
优势:
异常安全:传统方法new抛出异常时,对象出于无效状态。data已经被删除,但是分配失败
强异常安全性: 如果一个操作因为异常而失败,程序的状态会回滚到操作之前的样子,就像这个操作从来没执行过一样
代码复用:复用拷贝构造函数
自动资源管理:自动释放tmp资源
C++11 写法
1 2 3 4 5 6 MyString& operator =(MyString other) { std::swap (data,other.data); return *this ; }
移动构造还是拷贝构造?
左值:叫得出名字 右值:叫不出名字(临时变量,std::move)
左值用拷贝构造,右值用移动构造(偷)
模板 模板特化、偏特化 模板特化是为特定的模板参数提供特殊实现的技术。当通用模板不适合某些特定类型时,可以为其提供定制版本。
所有模板参数都指定具体类型就叫模板全特化
偏特化只对部分模板参数进行特化,函数模板不支持偏特化,只有类模板支持
函数模板特化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> #include <cstring> template <typename T>void print (T value) { std::cout << "General: " << value << std::endl; } template <>void print <const char *>(const char * value) { std::cout << "C-string: \"" << value << "\"" << std::endl; } template <>void print <int >(int value) { std::cout << "Integer: " << value << " (0x" << std::hex << value << ")" << std::endl; } int main () { print (3.14 ); print ("Hello" ); print (42 ); return 0 ; }
类模板特化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> template <typename T>class TypeInfo {public : static const char * name () { return "Unknown" ; } }; template <>class TypeInfo <int > {public : static const char * name () { return "int" ; } }; template <>class TypeInfo <double > {public : static const char * name () { return "double" ; } }; template <>class TypeInfo <const char *> {public : static const char * name () { return "const char*" ; } }; int main () { std::cout << TypeInfo<float >::name () << std::endl; std::cout << TypeInfo<int >::name () << std::endl; std::cout << TypeInfo<const char *>::name () << std::endl; return 0 ; }
偏特化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #include <iostream> template <typename T, typename U>class Pair {public : void print () { std::cout << "General Pair<T, U>" << std::endl; } }; template <typename T>class Pair <T, T> {public : void print () { std::cout << "Specialized Pair<T, T>" << std::endl; } }; template <typename T>class Pair <T, int > {public : void print () { std::cout << "Specialized Pair<T, int>" << std::endl; } }; int main () { Pair<double , char > p1; p1. print (); Pair<float , float > p2; p2. print (); Pair<std::string, int > p3; p3. print (); return 0 ; } template <typename T>class Wrapper {public : static const char * type () { return "Value" ; } }; template <typename T>class Wrapper <T*> {public : static const char * type () { return "Pointer" ; } }; template <typename T>class Wrapper <T**> {public : static const char * type () { return "Pointer-to-Pointer" ; } }; int main () { std::cout << Wrapper<int >::type () << std::endl; std::cout << Wrapper<int *>::type () << std::endl; std::cout << Wrapper<int **>::type () << std::endl; return 0 ; }
可变参数模板 基本用法:
1 2 3 4 template <typename ... Args> void function (Args... args) { }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <iostream> template <typename T>T sum (T value) { return value; } template <typename T, typename ... Args>T sum (T first, Args... args) { return first + sum (args...); } template <typename ... Args>std::size_t count (Args... args) { return sizeof ...(args); } int main () { std::cout << sum (1 , 2 , 3 , 4 , 5 ) << std::endl; std::cout << sum (1.5 , 2.5 , 3.5 ) << std::endl; std::cout << count (1 , 'a' , "hello" , 3.14 ) << std::endl; return 0 ; }
非类型模板参数(常量模板) cuda中用的多,用于编译器常量优化
1 2 3 4 5 6 7 8 9 10 template <int N>int sumArray (int (&arr)[N]) { int sum = 0 ; for (int i = 0 ; i < N; i++) sum += arr[i]; return sum; } int a[5 ] = {1 ,2 ,3 ,4 ,5 };int total = sumArray (a);
类型转换运算符 static_cast 静态转换 用于在编译时已知的,有逻辑关联的类型之间的转换
场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int i = 10 ;double d = static_cast <double >(i); class Base {};class Derived : public Base {};Derived derived; Base* basePtr = static_cast <Base*>(&derived); Base* basePtr = new Derived (); Derived* derivedPtr = static_cast <Derived*>(basePtr); class MyClass {public : MyClass (int x) {} }; int num = 5 ;MyClass obj = static_cast <MyClass>(num);
dynamic_cast动态转换 用于安全的在继承参差结构中进行向下转换或者交叉转换,依赖运行时类型信息RTTI
用法: 将基类指针或引用安全的转换为派生类的指针或者引用,必须用于多态类型(至少含有一个虚函数)
转换成功会返回目标类型的指针,转换失败返回null或者std::bad_cast
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Base { virtual void foo () {} }; class Derived : public Base {};Base* basePtr = new Derived (); Derived* derivedPtr = dynamic_cast <Derived*>(basePtr); if (derivedPtr != nullptr ) { } else { } Base* basePtr2 = new Base (); Derived* derivedPtr2 = dynamic_cast <Derived*>(basePtr2); try { Derived& derivedRef = dynamic_cast <Derived&>(*basePtr); } catch (const std::bad_cast& e) { std::cout << "转换失败: " << e.what () << std::endl; }
const_cast 常量转换 用于修改类型的 const 或 volatile 属性。这是唯一 能“去掉” const 属性的转换操作符
主要使用方式就是去掉const属性,以便接受一个非const参数但是不会修改还旧API
1 2 3 4 5 6 7 void printString (char * str) { std::cout << str << std::endl; } const char * myStr = "Hello, World!" ;printString (const_cast <char *>(myStr));
reinterpret_cast 重新解释转换 进行低级的、底层的、“重新解释”比特模式的转换。它可以将一个指针转换为任何其他类型的指针,甚至是一个整数。
不进行任何格式检查或安全性检查,可移植性差
场景
1 2 3 4 5 6 7 int * ip = new int (65 );char * cp = reinterpret_cast <char *>(ip);std::cout << *cp;