柚子快报邀请码778899分享:算法 开发语言 C++类复习

http://yzkb.51969.com/

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<::min()<

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++类复习

http://yzkb.51969.com/

相关链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。