柚子快报邀请码778899分享:算法 开发语言 C++类复习
C++类
1. 类内成员函数隐式声明为inline
class Str {
int x;
int y = 3;
public:
inline void fun(){
std::cout<<"pf,yes!"< } }; 这段代码不会报错,但是类内的成员函数隐式声明为inline函数,不需要单独写在前面。因此将成员函数在类的内部实现是最保险的写法。 2. 全局函数上使用inline。 inline int Add(int x, int y) { return x + y; } 这是全局函数上使用inline函数。 同样,上述代码可以写在头文件之中,或写在源文件之中,如果不使用 inline,那么写在头文件之中被多个源文件 include 那么就会导致符号重定义冲突,单个源文件引入却不会。 3. inline关键字的作用 可以在任意的函数定义的时候使用; 建议编译器使用内嵌的方式优化代码; inline函数,由调用的函数的源文件实现; 使用使用内联展开,取决于函数的复杂程度 以下的函数使用inline也不会被优化 1、函数指针或函数引用 2、复杂的 inline 函数体 3、虚函数 4、递归函数 举例说明如下 头文件中 #ifndef ICPP_HEADER_H #define ICPP_HEADER_H #include using namespace std; class Str { int x; int y = 3; public: void fun() { cout << "yes pf" << endl; } }; #endif //ICPP_HEADER_H main文件中 #include "header.h" #include using namespace std; int main() { int a = 0; int c = a + 7; Str str; str.fun(); std::cout << "Hello, World!" << std::endl; return 0; } 另外一个源文件中这样写 #include "header.h" #include 编译成功 但是如果,头文件中,类内声明,类外定义,就会报错。 #ifndef ICPP_HEADER_H #define ICPP_HEADER_H #include using namespace std; class Str { int x; int y = 3; public: void fun(); }; void Str::fun() { cout << "yes pf" << endl; } #endif //ICPP_HEADER_H 报错信息如下: /mnt/e/iCode/icpp/header.h:17: multiple definition of `Str::fun()'; CMakeFiles/icpp.dir/main.cpp.o:/mnt/e/iCode/icpp/header.h:17: first defined here collect2: error: ld returned 1 exit status 问题分析 关于函数的声明和定义可以写在头文件之中,也可以写在源文件之中,但如果不加inline,那么写在头文件之中被多个源文件 include 那么就会导致符号重定义冲突,单个源文件引入却不会。 如果仅有一个头文件,一个文件使用头文件中的类的外部定义,不会出现问题;类内定义和实现函数,不论有多少外部使用这个头文件的源文件都不会报错;多文件使用头文件时,类内声明,类外定义,将类外的定义声明成内联函数,使用inline关键字不会报错;掌握inline关键字的使用方法; 4. 分析下面几种写法 回答以下几个问题: 这个使用方法using my_int = int 类外声明的使用的使用方法,最好使用下面这种写法 inline auto Str::fun2()->my_int { { cout<<"my_int"< } } 举例如下: // // Created by pfw20 on 2024/3/31. // #ifndef ICPP_HEADER_H #define ICPP_HEADER_H #include using namespace std; class Str { using my_int = int; my_int m; int x; int y = 3; public: void fun(); my_int fun1(); my_int fun2(); }; inline Str::my_int Str::fun1() { { cout<<"my_int"< } } // 简化上述的代码另外一种写法 inline auto Str::fun2()->my_int { { cout<<"my_int"< } } inline int Add(int x, int y) { return x + y; } inline void Str::fun() { cout << "yes pf" << endl; } #endif //ICPP_HEADER_H 待说明: \#include \#include struct A{ // char a = 'a'; int c =10; }; int main(){ int x =10; char y = 'a'; std::cout< std::cout< std::cout< } 5. this指针,指向当前对象 在类的内部会有一个this指针,类型为class* this 隐藏指针,this 是一个常量,指针不能修改,指针指向的内容是可以修改。 修改类的成员所以可以使用this->成员,访问类内的成员, 用处: 使用this可以防止函数中的参数和类中的成员混淆基于const的成员函数重载 class Str{ void fun(int x){ } // 基于const的成员函数重载 void fun(int x) const{ // this 本身不能修改,可以修改指向的内容,加上const之后,指向的内容也是不能修改的。 } int x; }; & 基于引用的重载(不作重点记录) 这个和基于const的重载不能混淆使用。仅仅使用const或者仅仅使用引用 多个相同的变量出现在类的内部和全局中,使用类内部的可以使用this指针,全局的使用::即可 静态成员函数 所有的对象共享这个成员函数,每一个对象有自己的对象成员,不会出现交叉 静态成员就没有this指针,使用这个成员可以使用;类的操作域进行访问这个静态成员。 这个就可以将全局的常量定义为static的格式。 用处: 描述和类相关参数,和对象无关静态成员函数可以返回静态数据成员;静态成员函数操作静态成员 单例模式 class Str{ static auto& size(){ static Str x; return x; } } 6. 报错原因分析 为什么直接报错:/mnt/e/iCode/icpp/main.cpp:18: undefined reference to Str1::x’` #include class Str1{ public: static int x; int y; }; int main(){ std::cout< } 修改如下运行成功 #include class Str1{ public: inline static int x; int y; }; int main(){ std::cout< } C++特性封装:举例子洗衣机的电路板 7. C++特殊的成员函数 构造函数 代理构造函数 构造函数是可以重载的 C++11的代理构造函数,代理构造函数先执行 代理构造函数如下:(基本不使用,仅仅了解即可) \#include class Strp{ public: Strp():Strp(10){ std::cout<<"here1"< } Strp(int input){ x = input; std::cout<<"here2"< } private: int x; }; int main(){ Strp strp1; } 构造函数初始化(重要) 初始化列表,不使用缺省初始化 区分数据成员的初始化和赋值 在构造的时候通过列表初始化完成,提升系统的性能 #include #include class Strp { public: Strp(const std::string &val) : x(val), y(0) { std::cout< } private: std::string x; int y; }; int main() { Strp strp1("pf"); } 类中包含引用的成员,此时必须使用列表进行初始化 #include #include class Strp { public: Strp(const std::string &val,int& ref_i) : x(val) , y(0) ,ref(ref_i) { std::cout< ref =2; } private: std::string x; int y; int& ref; }; int main() { int val; Strp strp1("pf",val); } 元素的初始化顺序与其声明的顺序有关,与初始化列表中的顺序无关; 要求在初始化化列表时,顺序要一致,这里的一致就是先声明的先进行初始化,否则会包warnning 使用初始化列表覆盖类的内部的初始化信息 缺省的构造函数 不提供实参的构造函数 如果没有定义构造函数,编译器会提供一个缺省的构造函数,目的和C语言兼容,如果写一个就不会合成,如果类的成员中有引用成员,引用必须显示初始化。 缺省的构造函数会缺省构造函数进行缺省初始化。 调用缺省的构造函数避免(most vexing parse) 使用default定义,和内部的缺省的构造函数是一样的 举例子说明如下: 报错: #include #include class Strp { public: // Strp() = default; Strp(const std::string &input) : x(input) {} std::string x; }; int main() { Strp s; } No matching constructor for initialization of ‘Strp’ #include #include class Strp { public: Strp() = default; Strp(const std::string &input) : x(input) {} std::string x; }; int main() { Strp s; } 不再进行报错 或者下面这样写,也不会报错 #include #include class Strp { public: Strp(){} Strp(const std::string &input) : x(input) {} std::string x; }; int main() { Strp s; } 单一的参数构造函数 可以将其看成是一种类型转换 可以使用explicit关键字避免求值过程中的隐式转换 #include #include class Myclass { public: // explicit Myclass(const int& input): x(input) { } int x; }; int main() { Myclass my(3);// 直接参数列表进行初始化 Myclass m1 = 3;// // 拷贝初始化,涉及到类型的隐式转换,避免隐式转换使用explicit进行标注 Myclass m2{3}; Myclass m3(Myclass(3)); } 拷贝的构造函数(重要) 原有的对象来构造一个新的构造函数 不希望改变值,不能使用值传递,原因是会进行嵌套,死循环 拷贝构造函数的经典写法如果没有显示提供拷贝构造函数,编译器会自动生成一个,对每一个数据成员调用拷贝构造 #include #include class Myclass { public: // explicit // 全部考虑是引用传递,引用是对值的绑定 // 单一参数的构造函数 Myclass(const int &input) : x(input) { } // 缺省的构造函数 Myclass() = default; // 拷贝的构造函数的经典写法,为什么是const,为什么是引用 // Myclass(const Myclass&) = default; Myclass(const Myclass &m) : x(m.x) {// 这个也可以使用default } int x = 4; }; int main() { Myclass my(3);// 直接参数列表进行初始化 Myclass m1 = 3;// // 拷贝初始化,涉及到类型的隐式转换,避免隐式转换使用explicit进行标注 Myclass m2{3}; Myclass m3(Myclass(3)); } 移动构造函数(待更新) 柚子快报邀请码778899分享:算法 开发语言 C++类复习 相关链接
发表评论