C++知识点总结

一、C++简介

        1、c++的特点:

        1、在支持C语言的基础上,全面支持面向对象编程

        2、编程领域广泛,功能强大

        3、C++语言标准一直在保持更新

        4、支持底层操作的面向对象编程语言

        5、在面向对象编程语言中执行效率高

        2、面向过程与面向对象的区别

        面向过程是以“我想怎么解决”为核心;面向对象是以“我想让谁来解决这个问题”为核心。

        3、开发环境

        QtCreator

二、从C到C++

1、引用

1.1 概念

        引用类似于指针的平替,在所有面向对象的编程语言中都会被使用。引用相当于对某一目标变量起别名。

操作引用与操作原变量完全一样。

【问题】指针与引用的区别

        指针是一个变量,存储的是一个地址;引用变量是引用别名,本质跟原变量是同一个东西。

        引用必须被初始化且不能为NULL;指针声明时可以先不初始化且可以初始化为NULL

        引用初始化后不可再被改变,指针则可以改变指向的对象

        指针的大小是指针的大小,引用的大小是原变量的大小

1.2 引用的性质

        1、可以改变引用的值但是不能再次成为其他变量的引用

        2、声明引用时必须初始化,不能初始化为NULL

        3、声明引用时,初始化的值可以是纯数值,但此时需要用coust修饰,表示这是一个常量引用,此时引用的值不可被改变

2、赋值

        在C++中新增了一下赋值方法:

        int a (10);//相当于 int a=10,只能用于初始化

        int b (a);// int b=a

        int c (a+b);// int c=a+b

        double b = 3.14;

        int b1 = b;

        cout << b1 << endl; // 3 数据窄化

3、键盘输入 

可以使用cin把用户在命令行中的内容赋值到变量中。

cin和cout一样,都属于头文件iostream中的标准输入输出流,同时如果cin输入的字符串需要包含空格,则可以使用下面的方式:

#include <iostream>using namespace std;int main()
{// C++的字符串是stringstring str;cout << "请输入字符串,可以包含空格,输入完成后点击回车" << endl;getline(cin,str);   // 第二个参数只能是string类型cout << str << endl;return 0;
}

4、string字符串类

        string不是C++的基本数据类型,它是一个C++标准库中的字符串类,使用时需要引入对应的头文件,#include<string>,而不是string.h.

        string在绝大多数情况下,可以代替C语言中字符串,不必担心内存是否足够字符串长度等等。其中内部还包含了很多字符串处理函数,可以完成各种情况下的字符串处理功能。

        string和C语言相同,字符串编码使用ASCII编码,不支持中文。

#include <iostream>using namespace std;int main()
{string str = "helloworld";cout << str << endl;cout << str.size() << endl; // 10cout << str.length() << endl;   // 10cout << str[1] << endl; // ecout << str.at(1) << endl;  // ereturn 0;
}

at函数会在你输入要检索字符串大小超出原本大小时发出警告,程序运行停止。

        string类支持多种遍历方式

  • 普通循环(以for循环为主)
  • C++11:for each循环
#include <iostream>using namespace std;int main()
{string str = "helloworld";// 以for循环的方式进行输出字符串for(int i = 0; i < str.length(); i++){cout << str.at(i);}cout << endl;// 以 for each的方式进行循环遍历字符串for(char i:str){cout << i;}return 0;
}

        字符串与数字转换

#include <iostream>
#include <sstream> // 字符串流using namespace std;int main()
{string s = "123";
//    int i = s; 错误// string → intistringstream iss(s);int i;iss >> i;cout << i << endl;    // 123// int → string
//    string s2 = i; 错误stringstream ss;ss << i;string s2 = ss.str();cout << s2 << endl;return 0;
}

5、函数

5.1 内联函数

内联函数用于取代C语言中宏定义的函数,内联函数的正确使用可以提升程序的执行效率。内联函数在编译的时候,直接把函数体展开到主函数中编译,在运行期间可以减少调用的开销。

通常将具有以下性质的函数写为内联函数:

  • 代码长度5行以内
  • 不包含复杂的控制语句
  • 频繁被调用

关键字:inline

#include <iostream>using namespace std;// 内联函数
inline void pint_string(string str)
{cout << str << endl;
}int main()
{pint_string("hello world");return 0;
}

        即使加上了内联函数关键字inline 具体是不是内联函数依旧由编译器决定

5.2 函数重载 overload

        函数重载要求函数名称相同但是参数不同(包括类型、数量、前后顺序)。与返回值等其他因素无关。

#include <iostream>using namespace std;void print_show(int i)
{cout << "调用了int重载"<< i << endl;
}void print_show(float i)
{cout << "调用了float重载"<< i << endl;
}void print_show(double i)
{cout << "调用了double重载"<< i << endl;
}void print_show(string str)
{cout << "调用了string重载"<< str << endl;
}void print_show()
{cout << "调用了无参int重载"<< endl;
}int main()
{print_show(1);return 0;
}

5.3 哑元函数

        该函数的参数只有类型,没有名称

        作用一:哑元函数用来区分函数重载。

        作用二:运算符重载中用到。

#include <iostream>using namespace std;// 哑元函数
void print_show(int)
{cout << "调用了int的哑元函数" << endl;
}int main()
{print_show(56);return 0;
}

三、面向对象基础

1、类与对象

1.1概念

        类:类是一个抽象的概念,类是对象的特点

        对象:根据类创建的实体

1.2 类的内容

【例子】以手机为例

规定手机可以播放音乐、运行游戏、打电话、手机有品牌、型号、重量等属性

#include <iostream>using namespace std;// 帕斯卡命名法(大驼峰命名法)
// 每个单词的首字母大写
class MobilePhone
{
public:     // 权限:public最开放的权限string brand;   // 品牌string model;   // 型号int weight; // 重量void play_music(){cout << "只因你太美,哒哒哒" << endl;}void run_game(){cout << "原神启动、赛尔号、奇迹暖暖、三国杀、够级、保皇、金铲铲、咸鱼之王" << endl;}void call(){cout << "坤哥您好~~~" << endl;}};int main()
{return 0;
}

1.3对象的创建

栈内存对象:对象所在的{}执行完毕后,自动销毁

#include <iostream>using namespace std;// 帕斯卡命名法(大驼峰命名法)
// 每个单词的首字母大写
class MobilePhone
{
public:     // 权限:public最开放的权限string brand;   // 品牌string model;   // 型号int weight; // 重量void play_music(){cout << "只因你太美,哒哒哒" << endl;}void run_game(){cout << "原神启动、赛尔号、奇迹暖暖、三国杀、够级、保皇、金铲铲、咸鱼之王" << endl;}void call(){cout << "坤哥您好~~~" << endl;}};int main()
{MobilePhone mp; // 栈内存对象mp.brand = "华为";mp.model = "mate70pro Max";mp.weight = 2000;cout << mp.brand << mp.model << mp.weight << endl;mp.play_music();mp.run_game();mp.call();return 0;
}

        堆内存对象:必须使用new创建,使用指针保存,如果不使用delete关键字销毁,则内存堆像会出现内存泄漏。堆内存对象调用成员时,使用的是->

#include <iostream>using namespace std;// 帕斯卡命名法(大驼峰命名法)
// 每个单词的首字母大写
class MobilePhone
{
public:     // 权限:public最开放的权限string brand;   // 品牌string model;   // 型号int weight; // 重量void play_music(){cout << "只因你太美,哒哒哒" << endl;}void run_game(){cout << "原神启动、赛尔号、奇迹暖暖、三国杀、够级、保皇、金铲铲、咸鱼之王" << endl;}void call(){cout << "坤哥您好~~~" << endl;}};int main()
{MobilePhone *mp = new MobilePhone;  // 堆内存对象mp->brand = "小米";mp->model = "14 ultra";mp->weight = 1300;cout << mp->brand << mp->model << mp->weight << endl;mp->play_music();mp->run_game();mp->call();delete mp;  // 手动销毁mp = NULL;  // 指向空,防止野指针return 0;
}

2、封装

        封装指的是,将类的一些属性和细节隐藏,重新提供外部的访问接口。封装可以提升代码的安全性,并且可以让程序员更关注于上层架构而非内部细节。(依旧用上一个手机的例子)

#include <iostream>using namespace std;// 帕斯卡命名法(大驼峰命名法)
// 每个单词的首字母大写
class MobilePhone
{
private:        // 私有权限,最封闭的权限,只能在类内访问string brand;   // 品牌string model;   // 型号int weight = 200; // 重量public:     // 权限:public最开放的权限// get读函数string get_brand(){return brand;}// set写函数void set_brand(string b){brand = b;}// get读函数string get_model(){return model;}// set写函数void set_model(string b){model = b;}// get读函数int get_weight(){return weight;}
};int main()
{MobilePhone *mp = new MobilePhone;  // 堆内存对象mp->set_brand("老王手机");mp->set_model("隔壁");cout << mp->get_brand() << " " << mp->get_model() << " " << mp->get_weight() << endl;delete mp;return 0;
}

3、构造函数

3.1 基本使用

        构造函数是一种特殊的成员函数,用于创建对象时初始化,创建对象时必须直接或者间接调用当前类的任意一个构造函数。写法上有以下要求:

  • 函数名成与类名完全相同
  • 构造函数不写返回值类型,不写返回值
  • 如果程序员不手动编写构造函数,编译器会自动添加一个默认无参的构造函数。(手动编写构造函数后,编译器则不会添加无参构造函数)
  • 构造函数也同样支持函数重载、参数默认值
#include <iostream>using namespace std;// 帕斯卡命名法(大驼峰命名法)
// 每个单词的首字母大写
class MobilePhone
{
private:        // 私有权限,最封闭的权限,只能在类内访问string brand;   // 品牌string model;   // 型号int weight; // 重量public:     // 权限:public最开放的权限//    MobilePhone()
//    {
//        brand = "8848";
//        model = "M6巅峰版藏地棕牛皮";
//        weight = 512;
//    }MobilePhone(string b,string m,int w){brand = b;model = m;weight = w;}// get读函数string get_brand(){return brand;}// get读函数string get_model(){return model;}// get读函数int get_weight(){return weight;}
};int main()
{// 堆内存对象MobilePhone *mp = new MobilePhone("魅族","不知道",300);  // 堆内存对象cout << mp->get_brand() << " " << mp->get_model() << " " << mp->get_weight() << endl;delete mp;// 栈内存对象MobilePhone mp1("8848","M6巅峰版藏地棕牛皮",512);cout << mp1.get_brand() << mp1.get_model() << mp1.get_weight() << endl;return 0;
}

3.2 初始化构造列表

        初始化列表是一种更简单的给成员变量赋值的写法。

        当构造函数的局部变量(形参)与成员变量重名时,除了使用后面学习的this指针的方式外,也可以使用构造初始化列表区分。

#include <iostream>using namespace std;// 帕斯卡命名法(大驼峰命名法)
// 每个单词的首字母大写
class MobilePhone
{
private:        // 私有权限,最封闭的权限,只能在类内访问string brand;   // 品牌string model;   // 型号int weight; // 重量public:     // 权限:public最开放的权限// 无参构造函数,构造初始化列表MobilePhone():brand("8848"),model("M6巅峰版藏地棕牛皮"),weight(520){}// 构造初始化列表MobilePhone(string b,string m,int w):brand(b),model(m),weight(w){}// get读函数string get_brand(){return brand;}// get读函数string get_model(){return model;}// get读函数int get_weight(){return weight;}
};int main()
{// 栈内存对象MobilePhone mp1;cout << mp1.get_brand() << mp1.get_model() << mp1.get_weight() << endl;return 0;
}

3.3 隐式调用与显式调用

        构造函数的调用可以分为显式调用隐式调用

        显式调用指的是在创建对象时手写构造函数的名称及参数列表。

        隐式调用指的是在创建对象时不写构造函数的名称与参数列表,编译器会尝试调用对应参数的构造函数。

#include <iostream>using namespace std;class Student
{
private:int age;
public:Student(int a):age(a){cout << "构造函数" << endl;}Student(){cout << "无参构造函数" << endl;}int get_age(){return age;}
};int main()
{Student s1(12); // 显式调用cout << s1.get_age() << endl;Student s3 = Student(14);   // 显示调用cout << s3.get_age() << endl;//    Student s4 = 15;    // 隐式调用
//    cout << s4.get_age() << endl;Student *s2 = new Student(13);  // 显式调用cout << s2->get_age() << endl;Student s6;   // 默认调用无参构造函数return 0;
}

3.4 拷贝构造函数

3.4.1 概念

        当程序员不手写拷贝构造函数时,编译器会自动添加一个拷贝构造函数,使对象创建可以通过这个构造函数实现

#include <iostream>using namespace std;class Student
{
private:int age;
public:Student(int a):age(a){cout << "构造函数" << endl;}Student(){cout << "无参构造函数" << endl;}// 手写默认添加的拷贝构造函数Student(const Student &st){cout << "手写的拷贝构造函数被调用了" << endl;age = st.age;}int get_age(){return age;}
};int main()
{Student s1(12);cout << s1.get_age() << endl;   // 12Student s2(s1); // 调用拷贝构造函数cout << s2.get_age() << endl;   // 12return 0;
}

【思考】:拷贝构造函数是否存在隐患?

        存在,当成员变量存在指针类型时,默认的拷贝构造函数会导致两个对象的成员变量指向同一处,不符合面向对象的设计规范的。这种现象被称为”浅拷贝“。

3.4.2 浅拷贝

        所有的对象公用同一个地址,当改变一个对象的内容时,所有的对象内容都会被改变

#include <iostream>
#include <string.h>using namespace std;class Dog
{
private:char *name;
public:Dog(char *n){name = n;}void show_name(){cout << name << endl;}
};int main()
{char arr[20] = "旺财";Dog d1(arr);Dog d2(d1); // 拷贝构造函数strcpy(arr,"大黄");   // 浅拷贝更改外部内存,对象内的数据,也被更改。因为操作的是同一块内存d1.show_name();d2.show_name();return 0;
}

        这种情况必需手动编写拷贝构造函数,使每次赋值都创建一个新的副本,从而让每个对象单独持有自己的成员变量,这种方式也称作深拷贝。

3.4.3 深拷贝

#include <iostream>
#include <string.h>using namespace std;class Dog
{
private:char *name;
public:Dog(char *n){name = new char[20];strcpy(name,n);}Dog(const Dog &d){name = new char[20];strcpy(name,d.name);}void show_name(){cout << name << endl;}
};int main()
{char arr[20] = "旺财";Dog d1(arr);Dog d2(d1); // 拷贝构造函数strcpy(arr,"大黄");   // 浅拷贝更改外部内存,对象内的数据,也被更改。因为操作的是同一块内存d1.show_name(); // 旺财d2.show_name(); // 旺财return 0;
}
3.4.4 析构函数

        析构函数是与构造函数完全对立的,用于销毁对象释放资源

构造函数

析构函数

创建对象时手动调用

当对象销毁时,自动调用

函数名称是类名

函数名称~类名

构造函数可以重载

析构函数没有参数,不能重载

用于创建对象时并初始化

用于销毁对象释放资源

有返回值但是不写,返回值时新创建的对象

没有返回值

#include <iostream>
#include <string.h>using namespace std;class Dog
{
private:char *name;
public:Dog(char *n){name = new char[20];strcpy(name,n);}Dog(const Dog &d){name = new char[20];strcpy(name,d.name);}void show_name(){cout << name << endl;}~Dog(){cout << "析构函数被调用了" << endl;delete []name;}};int main()
{char arr[20] = "旺财";Dog d1(arr);Dog d2(d1); // 拷贝构造函数strcpy(arr,"大黄");   // 浅拷贝更改外部内存,对象内的数据,也被更改。因为操作的是同一块内存d1.show_name(); // 旺财d2.show_name(); // 旺财return 0;
}

【思考】:

如果类中不写任何权限,默认什么权限?

        私有权限

如果不手动添加任何函数,编译器会自动添加哪些函数?

        无参构造函数、拷贝构造函数、析构函数

如果添加了无参构造函数,默认的无参构造函数还会添加嘛?

        不会

如果添加了任意一个构造函数,默认无参构造函数会添加嘛?

        不会

如果添加了拷贝构造函数,默认无参构造函数会添加嘛?

        不会

如果添加了默认无参构造函数,拷贝构造函数还会帮你添加嘛?

        会

如果添加了有参构造函数,拷贝构造函数还会帮你添加嘛?

        会

3.5 作用域限定符:: 

3.5.1 名字空间
#include <iostream>using namespace std;namespace my_space
{int a = 3;int b = 4;
}int a = 2;using namespace my_space;int main()
{int a = 1;cout << a << endl;  // 1 就近原则cout << ::a << endl;    // ::匿名名字空间。2cout << my_space::a << endl;    // 3cout << b << endl;  // 4return 0;
}
3.5.2 类内声明,类外定义
#include <iostream>using namespace std;class Demo
{
public:// 类内声明Demo();void test(string str);
};// 类外定义
Demo::Demo()
{cout << "构造函数" << endl;
}void Demo::test(string str)
{cout << "str= " << str << endl;
}int main()
{Demo d;d.test("hello");return 0;
}

3.6 this指针

3.6.1 概念

        this指针指向的是当前类对象的首地址

#include <iostream>using namespace std;class Test
{
public:void test_this(){cout << this << endl;}
};int main()
{Test t1;cout << &t1 << endl;    // 0x61fe8ft1.test_this();         // 0x61fe8fTest t2;cout << &t2 << endl;    // 0x61fe8et2.test_this();         // 0x61fe8eTest *t3 = new Test;cout << t3 << endl;     // 0x732808t3->test_this();        // 0x732808delete t3;return 0;
}
3.6.2 功能
3.6.2.1 类内调用成员

        成员(变量+函数)必须由对象调用,类内调用成员都依赖this指针,通常由编译器自动添加

#include <iostream>using namespace std;class Test
{
private:string name;
public:Test(string n){// 编译器默认添加this指针指向当前的对象,调用成员this->name = n;}string get_name(){// 编译器默认添加this指针指向当前的对象,调用成员return this->name;}void test_this(){cout << this << endl;}
};int main()
{Test t1("zhangsan");cout << t1.get_name() << endl;return 0;
}
3.6.2.2 区别重名的成员变量与局部变量
#include <iostream>using namespace std;class Test
{
private:string name;
public:Test(string name)/*:name(name)*/    // 构造初始化列表也可以区分{// 通过this指针在函数体中进行区分this->name = name;}string get_name(){// 编译器默认添加this指针指向当前的对象,调用成员return this->name;}void test_this(){cout << this << endl;}
};int main()
{Test t1("zhangsan");cout << t1.get_name() << endl;return 0;
}

6.2.3 链式调用

支持链式调用的成员函数特点:

  1. 当一个成员函数的返回值是当前类型的引用时,往往这个函数就支持链式调用。
  2. return 后面是*this
#include <iostream>using namespace std;class Test
{
private:int val = 0;
public:Test& add(int i){val += i;   // val = val + i;return *this;   // this是一个指针,取内容返回当前对象}int get_val(){return val;}};int main()
{Test t1;t1.add(1);t1.add(2);t1.add(100);cout << t1.get_val() << endl;   // 103// 链式调用Test t2;cout << t2.add(2).add(32).add(200).get_val() << endl;   // 234cout << t2.get_val() << endl;   // 234return 0;
}

7、static关键字

7.1 静态局部变量

        使用static修饰局部变量,这样的变量就是静态局部变量。

        静态局部变量在第一次调用时创建,直到程序结束后销毁,同一个类的所有对象共用这一份静态局部变量。

#include <iostream>using namespace std;class Test
{
public:void func(){int a = 1;static int b = 1;   // 静态局部变量cout << "a = " << ++a << " " << &a << endl;cout << "b = " << ++b << " " << &b << endl;}
};int main()
{Test t1;t1.func();t1.func();cout << "--------------" << endl;Test t2;t2.func();Test t3;t3.func();return 0;
}

7.2 静态成员变量

        使用static修饰成员变量,这样的变量就是静态成员变量。

        静态成员变量需要类内声明。类外初始化。

        一个类的所有对象共用一份静态成员变量,虽然静态成员变量可以使用对象调用,但是更建议使用类名直接调用。所以静态成员变量可以脱离对象使用,在程序开始运行时就开辟内存空间,直到程序运行结束后销毁。

        更推荐直接使用类名调用。代码的可读性更好。

#include <iostream>using namespace std;class Test
{
public:int a = 1;
//    static int b = 2; // 错误,静态成员变量需要类内声明。类外初始化static int b;   // 类内声明
};// 类外初始化
int Test::b = 1;int main()
{cout << Test::b << " " << &Test::b << endl; // 1 0x403004Test t1;cout << t1.a++ << " " << &t1.a << endl; // 1 0x61fe8ccout << t1.b++ << " " << &t1.b << endl; // 1 0x403004cout << t1.a++ << " " << &t1.a << endl; // 2 0x61fe8ccout << t1.b++ << " " << &t1.b << endl; // 2 0x403004cout << "------------------" << endl;Test t2;cout << t2.a++ << " " << &t2.a << endl; // 1 0x61fe88cout << t2.b++ << " " << &t2.b << endl; // 3 0x403004cout << Test::b << " " << &Test::b << endl;// 4 0x403004return 0;
7.2 静态成员函数

        使用static修饰成员函数,这样的函数就是静态成员函数。

        与静态成员变量相似的有:

  • 都可以通过类名直接调用,也可以通过对象调用。(推荐使用类名直接调用)
  • 可以脱离对象使用
  • 如果再静态成员函数中调用非静态成员。可以通过参数将对象传递进来,因为静态成员函数没有this指针。
  • 也可以通过再静态成员函数中,创建新的对象进行调用。

        因此,静态成员函数没有this指针,不能再静态成员函数中调用同类其他非静态成员,但是静态成员函数可以调用静态成员。

#include <iostream>using namespace std;class Test
{
public:void func0(){
//        func1(); cout << "非静态成员函数" << endl;}static void func1(){
//        func0(); // 错误,静态成员函数,不能调用非静态成员cout << "静态成员函数" << endl;}static void func2(){cout << "静态成员函数2" << endl;}
};int main()
{
//    Test::func1();Test t1;
//    t1.func0();t1.func1();
//    t1.func2();return 0;
}

8.const关键字

8.1 const修饰成员函数

        cosnt修饰的成员函数,表示常成员函数。

特性如下:

  • 可以调用成员变量,但是不能修改成员变量的数值。
  • 不能调用非const修饰的成员函数,哪怕这个函数并没有改变成员变量。

        建议只要成员函数不修改成员变量,就是用const修饰,例如get、print、show等显式函数等。

#include <iostream>using namespace std;class Demo
{
private:int a;
public:Demo(int a){this->a = a;}void func0(){cout << "哈哈哈哈哈" << endl;}int get_demo()const{return a;}void test()const{
//        int a = 10;
//        a++;
//        a++;    // 错误const修饰的成员函数,不能修改成员变量cout << a << endl;  // 可以调用但是不能修改//        func0(); // 错误const修饰的成员函数,不能调用非const修饰的成员函数get_demo();}};int main()
{Demo d1(1);d1.test();return 0;
}
8.2 const修饰对象

        const修饰的对象被称为常量对象,这种对象的成员变量值无法修改,也无法调用非const的成员函数。

#include <iostream>using namespace std;class Demo
{
private:int a;
public:int b = 1;Demo(int a){this->a = a;}void func0(){cout << "哈哈哈哈哈" << endl;}int get_demo()const{return a;}};int main()
{
//    const Demo demo(1);Demo const demo(1); // 两种初始化方式,等效于上一行cout << demo.get_demo() << endl;//    demo.func0();     // 错误const修饰的对象,无法调用非const修饰的成员函数cout << demo.b << endl;
//    demo.b = 10; // 错误 const修饰的对象,无法修改成员变量return 0;
}
8.3 const修饰成员变量

        const修饰的成员变量,常成员变量,表示该成员变量的值无法被修改。

        常成员变量有两种初始化的方式:

  • 直接赋值

        声明后赋值:

  • 构造初始化列表

        上述两种方式同时使用时,前者失效,以后者为准(构造初始化列表)

#include <iostream>using namespace std;class Demo
{
private:const int a = 1;    // 直接赋值const int b = 2;const int c = 3;public:Demo(int a,int b,int c):a(a),b(b),c(c){
//        this->a = a;    // 二次赋值
//        this->b = b;
//        this->c = c;}void show(){cout << a << b << c << endl;}void test(){
//        a++;
//        b++;
//        c++;}};int main()
{Demo d1(10,20,30);d1.show();d1.test();return 0;
}
8.4 const修饰局部变量

const修饰局部变量,表示该局部变量无法被修改,这种方法常用于引用参数

#include <iostream>using namespace std;class Demo
{
private:const int a = 1;    // 直接赋值const int b = 2;const int c = 3;public:Demo(int a,int b,int c):a(a),b(b),c(c){
//        this->a = a;    // 二次赋值
//        this->b = b;
//        this->c = c;}void show(){cout << a << b << c << endl;}void test(){const int e = 1;    // const修饰局部变量
//        e = 2; 错误}
};int main()
{Demo d1(10,20,30);d1.show();d1.test();return 0;
}
8.5 const修饰引用参数
#include <iostream>using namespace std;class Demo
{
private:const int a = 1;    // 直接赋值const int b = 2;const int c = 3;public:Demo(int a,int b,int c):a(a),b(b),c(c){
//        this->a = a;    // 二次赋值
//        this->b = b;
//        this->c = c;}void show(){cout << a << b << c << endl;}void test(const int &f){const int e = 1;    // const修饰局部变量
//        e = 2; 错误}};int main()
{Demo d1(10,20,30);d1.show();int g = 20;d1.test(g);return 0;
}

四、运算符重载

1、友元

1.1 概念

        友元函数是一种定义在类外的普通函数,但是在调用时需要在类内声明,目的是为了与该类成员函数加以区分,声明时在函数名前添加friend关键字,友元函数不是成员函数,但是他可以访问类中所有成员函数,包括私有成员。

        友元有三种实现方式:

  • 友元函数
  • 友元类
  • 友元成员函数

        友元在于提高程序的运行效率,但是,它破坏了类的封装性和隐藏性,使得非成员函数能够访问类中的私有成员。导致程序的维护性变差,使用友元要慎重。

1.2 友元函数

        友元函数不属于任何一类,是类外的普通函数,可以访问类内所有成员,但是需要在类中进行声明。

        友元函数的使用需要注意以下几点:

  • 友元函数没有this指针
  • 友元函数的”声明“可以放置到类中的任何位置,不受权限修饰符的影响
  • 一个友元函数理论上可以访问多个类,只需要在各个类中分别”声明“。
#include <iostream>using namespace std;class Test
{
private:int a;
public:Test(int i):a(i){}void show(){cout << a << " " << &a << endl;}// 友元函数,类内声明friend void and_test(Test &t);
};// 友元函数
void and_test(Test &t)
{
//   t.show();cout << ++t.a << " " << &t.a << endl;
}int main()
{Test t1(1);and_test(t1);t1.show();return 0;
}

1.3 友元类

        当一个类B成为另一个类Test的朋友是,B可以访问Test中所有的成员,此时认定B是Test的友元类。

        友元类的使用需要注意以下几点:

  • 友元关系不能被继承
  • 友元关系不具有交换性(比如:类B声明成类Test的友元,类B可以访问类Test中的成员,但是类Test不能访问类B中的私有成员,如果需要访问,需要将类Test声明成类B的友元)

        互为友元,需要类内声明,类外实现。

#include <iostream>using namespace std;class Test
{
private:int a;
public:Test(int i):a(i){}void show(){cout << a << " " << &a << endl;}// 友元类,类内声明friend class B;
};class B
{
public:void and_test(Test &t){cout << ++t.a << " " << &t.a << endl;}
};int main()
{Test t1(1);B b;b.and_test(t1);t1.show();return 0;
}

1.4 友元成员函数

#include <iostream>using namespace std;// 第四步:声明被访问的类
class Test;class B
{
public:// 第二步:声明友元成员函数(类内声明,类外实现)void and_test(Test &t);
};class Test
{
private:int a;
public:Test(int i):a(i){}void show(){cout << a << " " << &a << endl;}// 友元成员函数,类内声明:第一步确定友元成员函数的格式并声明friend  void B::and_test(Test &t);
};// 第三步:类外定义友元成员函数
void B::and_test(Test &t)
{cout << ++t.a << " " << &t.a << endl;
}int main()
{Test t1(1);B b;b.and_test(t1);t1.show();return 0;
}

2、运算符重载

2.1  概念

        C++中可以把部分运算符看做成函数,此时运算符也可以重载。

        运算符预定义的操作只能针对基本数据类型,但是对于自定义类型,也需要类似的运算操作,此时就可以重新定义这些运算符的功能,使其支持特定类型,完成特定的操作。

        运算符重载有两种实现方式:

  • 友元函数运算符重载
  • 成员函数运算符重载

2.2 友元函数运算符重载

#include <iostream>using namespace std;class MyInt
{
private:int a;
public:MyInt(int a):a(a){}int get_int(){return a;}// + 运算符重载friend MyInt operator+(MyInt &i,MyInt &i2);
};MyInt operator+ (MyInt &i,MyInt &i2)
{
//    MyInt int4(0);
//    int4.a = i.a + i2.a;
//    return int4;return i.a + i2.a;
}int main()
{MyInt int1(2);cout << int1.get_int() << endl; // 2MyInt int2(int1);   // 拷贝构造函数cout << int2.get_int() << endl; // 2MyInt int3 = int1 + int2;cout << int3.get_int() << endl;return 0;
}
2.2.1 ++运算符重载
#include <iostream>using namespace std;class MyInt
{
private:int a;
public:MyInt(int a):a(a){}int get_int(){return a;}// + 运算符重载friend MyInt operator+(MyInt &i,MyInt &i2);// ++ 运算符重载friend MyInt operator ++(MyInt &i); // 前置自增friend MyInt operator ++(MyInt &i,int); // 后置自增
};MyInt operator+ (MyInt &i,MyInt &i2)
{
//    MyInt int4(0);
//    int4.a = i.a + i2.a;
//    return int4;return i.a + i2.a;
}MyInt operator ++(MyInt &i)
{++i.a;return i;
}MyInt operator ++(MyInt &i,int)
{return i.a++;
}int main()
{MyInt int1(2);cout << int1.get_int() << endl; // 2MyInt int2(int1);   // 拷贝构造函数cout << int2.get_int() << endl; // 2MyInt int3 = int1 + int2;cout << int3.get_int() << endl;++int1;cout << int1.get_int() << endl; // 3cout << (int1++).get_int() << endl; // 3cout << int1.get_int() << endl; // 4return 0;
}

2.3 成员函数运算符重载

        成员函数运算符重载相比于友元函数重载,最主要的区别在于,友元函数的第一个输出参数,在成员函数中使用this指针代替,因此同样的运算符重载,成员函数运算符重载比友元函数运算符重载参数少一个。

#include <iostream>using namespace std;class MyInt
{
private:int a;
public:MyInt(int a):a(a){}int get_int(){return a;}// + 运算符重载MyInt operator+(MyInt &i2);// ++ 运算符重载MyInt operator ++(); // 前置自增MyInt operator ++(int); // 后置自增
};MyInt MyInt::operator+ (MyInt &i2)
{return this->a + i2.a;
}MyInt MyInt::operator ++()
{return ++this->a;
}MyInt MyInt::operator ++(int)
{return this->a++;
}int main()
{MyInt int1(2);cout << int1.get_int() << endl; // 2MyInt int2(int1);   // 拷贝构造函数cout << int2.get_int() << endl; // 2MyInt int3 = int1 + int2;cout << int3.get_int() << endl;++int1;cout << int1.get_int() << endl; // 3cout << (int1++).get_int() << endl; // 3cout << int1.get_int() << endl; // 4return 0;
}

2.4 特殊运算符重载

2.4.1 赋值运算符重

        除了之前学习的无参构造函数、拷贝构造函数与析构函数以外,如果程序员不手写,编译器还会给一个类添加赋值运算符重载函数。

        赋值运算符重载只能使用成员函数运算符重载。

        当类中出现指针类型的成员变量时,默认的赋值运算符重载函数类似于默认的浅拷贝构造函数出现的问题。因此也需要手写编写解决”浅拷贝“的问题。

#include <iostream>using namespace std;class MyInt
{
private:int a;
public:MyInt(int a):a(a){}int get_int(){return a;}// 编译器自动添加的赋值运算符重载函数MyInt & operator =(MyInt &i){cout << "赋值运算符重载函数被调用了" << endl;this->a = i.a;return *this;}};int main()
{MyInt int1(2);MyInt int2(3);MyInt int3 = int1;   // 拷贝构造函数隐式调用int3 = int2;    // 赋值运算符重载cout << int3.get_int() << endl;return 0;
}
2.4.2 类型转换运算符重载        

        必须使用成员函数运算符重载,且格式比较特殊

#include <iostream>using namespace std;class MyInt
{
private:int a;string str = "hello";
public:MyInt(int a):a(a){}int get_int(){return a;}// 编译器自动添加的赋值运算符重载函数MyInt & operator =(MyInt &i){cout << "赋值运算符重载函数被调用了" << endl;this->a = i.a;return *this;}// 类型转换运算符重载operator int(){return a;}operator string(){return str;}};int main()
{MyInt int1(2);int a = int1;cout << a << endl;string str  = int1;cout << str << endl;return 0;
}

2.5 注意事项

  • 重载运算符限制在C++语言中的已有的运算符范围,不能创建新的运算符。
  • 运算符重载本质上也是函数重载, 但是不支持函数参数默认值设定。
  • 重载之后的运算符不能改变运算符的优先级和结合性。也不能改变运算符的操作数和语法结构。
  • 运算符重载必须基于或包含自定义类型,即不能改变基本数据类型的运算规则。
  • 重载的功能应该与原有功能相似,避免没有目的的滥用运算符重载。
  • 一般情况下,双目运算符建议使用友元函数运算符重载,单目运算符建议使用成员函数运算符重载。

五、模板与容器

1、模板

        模板可以让类或者函数支持一种通用类型,这种通用的类在实际运用中可以是用任何数据类类型,因此程序员可以写出一些与类无关的代码,这种编程方式也被成为“泛型编程”

        通常有两种表示形式:

  • 函数模板
  • 类模板

1.1 函数模板

        使一个函数支持模板编程,可以让函数支持通用数据类型

#include <iostream>
#include <string.h>using namespace std;template<class T>   // 可以是class也可以是typename
T add(T a, T b)
{return a + b;
}int main()
{string str = "10";string str2 = "20";cout << add(2,3) << endl;return 0;
}

1.2 类模板

        使一个类支持模板编程的模型,可以使一个类支持通用数据类型

#include <iostream>
#include <string.h>using namespace std;template<class T>   // 可以是class也可以是typename
T add(T a, T b)
{return a + b;
}template<class T>   // 可以是class也可以是typename
class Test
{
private:T val;
public:Test(T v):val(v){}T get_val()const{return val;}void set_val(const T &val){this->val = val;}
};int main()
{
//    Test t1(20);
//    cout << t1.get_val() << endl;Test<int> t1(20);cout << t1.get_val() << endl;t1.set_val(2.354);cout << t1.get_val() << endl;   // 数据窄化输出2Test<double> t2(0.2);cout << t2.get_val() << endl;   // 0.2Test<string> t3("你好");cout << t3.get_val() << endl;return 0;
}

2、容器

2.1 标准模板库STL

        标准模板库(Standard Template Library,STL)是惠普实验室开发的一系列软件的统称。虽说它主要出现到了C++中,但是在被引入C++之前该技术就已经存在了很长时间。

        STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。

2.2 概念

        容器是用来存储数据的集合,数据元素可以是任意类型(基于模板实现),使用时要引入对应头文件

2.3 顺序容器

        该容器中每个元素都有固定位置且呈线性排布,除非使用者删除或者插入来改变元素位置

2.3.1 array数组

        array数组是C++11新增的容器类型,与传统数组相比更加安全,易于使用。array数组是定长的。没有办法方便的进行伸缩。

#include <iostream>
#include <array>using namespace std;int main()
{// 创建一个长度为5的int数组array<int,5> arr = {1,54,3}; // 后面两位补零cout << arr[0] << endl; // 1cout << arr[4] << endl; // 0// 推荐使用cout << arr.at(2) << endl;  // 3arr[3] = 200;// for循环for(int i = 0; i < arr.size(); ++i){cout << arr.at(i) << endl;}// for eachfor(int i : arr){cout << i << endl;}// 迭代器遍历,后边讲return 0;
}
2.3.2 vector向量

        内部由数组实现,擅长存取,不擅长删插

#include <iostream>
#include <vector>using namespace std;int main()
{//    vector<int> v = {1,2,3};//    cout << v.size() << endl;//    for(int i : v)//    {//        cout << i << endl;//    }// 创建一个长度为5的int向量vector<int> vec(5);//    for(int i : vec)//    {//        cout << i << endl;//    }// 增vec.push_back(222);
//    cout << vec.size() << endl; // 6vec.insert(vec.begin()+2,333);  // begin()可以返回指向第一个元素的迭代器指针,+2是在第三个位置上插入333// 改vec.at(0) = 111;vec[1] = 222;vec.at(3) = 444;vec.at(4) = 555;vec.at(5) = 666;// 删除// 删除最后一个元素vec.pop_back();vec.erase(vec.begin()+1);   // 删除第二个元素vec.erase(vec.end()-2); // 删除倒数第二个元素// 查cout << vec[0] << endl;cout << vec.at(1) << endl;for(int i : vec){cout << i << " ";}cout << endl;// 遍历for(int i = 0; i < vec.size(); ++i){cout << vec.at(i) << endl;}// 迭代器遍历,先省略// 判断是否为空,1空,0就是非空cout << vec.empty() << endl; // 0// 清空vec.clear();cout << vec.empty() << endl;    // 1cout << vec.size() << endl; // 0return 0;
}
2.3.3 list列表

        list由双向链表实现,内存空间不连续,不支持下标访问。擅长删、插,不擅长存取

#include <iostream>
#include <list>using namespace std;int main()
{//    // 创建了一个默认无数值的list//    list<string> lis1;//    // 创建一个长度为2的列表,第一个元素hello,第二个元素world//    list<string> lis2{"hello","world"};//    for(string s : lis2)//    {//        cout << s << endl;//    }// 创建一个长度为5的列表,每个元素都是hellolist<string>lis(5,"hello");// 增lis.push_back("world"); // 向后追加单元素lis.push_front("haha"); // 向前插入单元素lis.insert(++lis.begin(),"222");    // 在第二个位置上插入“222”// 删除// lis.pop_back(); // 删除最后一个元素lis.pop_front();    // 删除第一个元素// 迭代器操作// 保存迭代器指针list<string>::iterator iter = lis.begin();advance(iter,1);    // 移动迭代器lis.insert(iter,"333"); // 插入333// 删除最后一个元素
//    iter = lis.end();
//    iter--;
//    lis.erase(iter);iter = lis.begin();advance(iter,1);lis.erase(iter);// 返回第一个元素的引用cout << lis.front() << endl;// 返回最后一个元素的引用cout << lis.back() << endl;// 改iter = lis.end();advance(iter,2);*iter = "200";// 查cout << "-----" << *iter << endl;// 不支持普通循环,因为不支持下标// for each 可以使用for(string s : lis){cout << s << endl;}// 也支持迭代器遍历// 清空lis.clear();cout << lis.size() << endl; // 0return 0;
}
2.3.4 deque队列

        deque几乎支持所有vector的API,性能位于vector与list两者之间。最擅长两端存取的顺序容器。

#include <iostream>
#include <deque>using namespace std;int main()
{//    deque<int> v = {1,2,3};//    cout << v.size() << endl;//    for(int i : v)//    {//        cout << i << endl;//    }// 创建一个长度为5的int向量deque<int> deq(5);//    for(int i : deq)//    {//        cout << i << endl;//    }// 增deq.push_back(222);
//    cout << deq.size() << endl; // 6deq.insert(deq.begin()+2,333);  // begin()可以返回指向第一个元素的迭代器指针,+2是在第三个位置上插入333// 改deq.at(0) = 111;deq[1] = 222;deq.at(3) = 444;deq.at(4) = 555;deq.at(5) = 666;// 删除// 删除最后一个元素deq.pop_back();deq.erase(deq.begin()+1);   // 删除第二个元素deq.erase(deq.end()-2); // 删除倒数第二个元素// 查cout << deq[0] << endl;cout << deq.at(1) << endl;for(int i : deq){cout << i << " ";}cout << endl;// 遍历for(int i = 0; i < deq.size(); ++i){cout << deq.at(i) << endl;}// 迭代器遍历,先省略// 判断是否为空,1空,0就是非空cout << deq.empty() << endl; // 0// 清空deq.clear();cout << deq.empty() << endl;    // 1cout << deq.size() << endl; // 0return 0;
}

2.4 关联容器

        管两天容器中各个元素之间没有严格顺序,但是其中的键值具有唯一性,通过键可以找到对应的值

#include <iostream>
#include <map>using namespace std;int main()
{// 创建了一个map容器的对象ma1// 列表初始化,C++11支持map<string,int> ma1 = {{"年龄",18},{"体重",200}}; // 有两个元素cout << ma1.size() << endl; // 2// 创建一个元素为0的键值对对象map<string,int> ma;cout << ma.size() << endl;  // 0// 增ma["身高"] = 226; // 插入元素ma.insert(pair<string,int>("体重",300));  // 插入元素// 查cout << ma["身高"] << endl;   // 226cout << ma["体重"] << endl;   // 300if(ma.find("身高") == ma.end())   // find从头开始查找,查找不到返回end{cout << "没有找到身高元素" << endl;}else{cout << ma["身高"] << endl;}// 改ma["身高"] = 50;  // 修改元素cout << ma["身高"] << endl;// 删,删除之前可以判断元素是否存在
//    int re = ma.erase("身高");    // 删除成功返回1,失败返回0
//    cout << "身高删除的返回值:" <<re << endl;//    re = ma.erase("身高");
//    cout << "身高删除的返回值:" <<re << endl;   // 0//    cout << ma.size() << endl;  // 1//    ma.clear();
//    cout << ma.size() << endl;// for eachfor(pair<string,int>pa : ma){cout << pa.first << " " << pa.second << endl;}return 0;
}

2.5 迭代器遍历

        迭代器使特殊的指针,用于容器中元素的读写以及遍历

        如果迭代器不对数值进行修改,建议只使用只读迭代器 const_iterator, 返回值用 iterator

#include <iostream>
#include <map>
#include <array>
#include <vector>
#include <list>
#include <deque>using namespace std;int main()
{string s = "abcdefg";// 迭代器遍历stringfor(string::const_iterator iter = s.begin(); iter != s.end();iter++){cout << *iter << endl;}cout << "------------------" << endl;array<int,5> arr = {23,45,66,22,34};for(array<int,5>::const_iterator iter = arr.begin();iter != arr.end();++iter){cout << *iter << endl;}cout << "------------------" << endl;vector<string>vec(6,"hello");for(vector<string>::const_iterator iter = vec.begin();iter != vec.end();iter++){cout << *iter << endl;}cout << "------------------" << endl;list<string> lis(6,"world");for(list<string>::const_iterator iter = lis.begin();iter != lis.end();iter++){cout << *iter << endl;}cout << "------------------" << endl;deque<string>de(6,"hahaha");for(deque<string>::const_iterator iter = de.begin();iter != de.end();iter++){cout << *iter << endl;}cout << "------------------" << endl;map<string,int> ma;ma["年龄"] = 20;ma["身高"] = 185;ma["体重"] = 75;for(map<string,int>::const_iterator iter = ma.begin(); iter != ma.end(); iter++){// first 是键,second是值cout << iter->first << " " << iter->second << endl;}return 0;
}

六、面向对象核心

1、继承

1.1概念

        继承使面向对象的三大特性之一。体现了代码复用的思想

        继承就是在一个类的基础上新建一个类,并拥有之前一个类的特性

  • 已存在的类被称为“基类”或者“父类”
  • 新建立的被称为“派生类”或者“子类”
#include <iostream>using namespace std;// 基类
class Father
{
private:string name = "孙";
public:void set_name(string name){this->name = name;}string get_name(){return name;}void work(){cout << "开挖掘机" << endl;}
};// 派生类
class Son:public Father
{};int main()
{Son son;cout << son.get_name() << endl;son.work();return 0;
}

        面的代码,Son类的功能几乎与Father类重叠。在实际的使用过程中,派生类会做出一些与基类的差异化。

  • 修改继承来的基类内容

        属性:1、公有属性可以直接更改。更改后基类中的属性也会被改变。因为改的是同一份变量。私有属性,需要使用基类提供的公有函数进行更改。

        行为:函数隐藏,通过派生类实现一个同名同参数的函数,来隐藏基类的函数。

  • #include <iostream>using namespace std;// 基类
    class Father
    {
    private:string name = "孙";
    public:void set_name(string name){this->name = name;}string get_name(){return name;}void work(){cout << "开挖掘机" << endl;}
    };// 派生类
    class Son:public Father
    {
    public:void inti(){set_name("王");}void work(){cout << "我不光干活,我还唱跳rap打篮球" << endl;}void game(){cout << "原神启动!!!!!" << endl;}};int main()
    {Son son;son.inti();cout << son.get_name() << endl; //  王son.work(); // 我不光干活,我还唱跳rap打篮球son.game(); // 原神启动!!!!!son.Father::work(); // 开挖掘机,调用基类被隐藏的成员函数return 0;
    }
    
    新增派生类的内容
#include <iostream>using namespace std;// 基类
class Father
{
private:string name = "孙";
public:void set_name(string name){this->name = name;}string get_name(){return name;}void work(){cout << "开挖掘机" << endl;}
};// 派生类
class Son:public Father
{
public:void inti(){set_name("王");}void work(){cout << "我不光干活,我还唱跳rap打篮球" << endl;}void game(){cout << "原神启动!!!!!" << endl;}};int main()
{Son son;son.inti();cout << son.get_name() << endl; //  王son.work(); // 我不光干活,我还唱跳rap打篮球son.game(); // 原神启动!!!!!son.Father::work(); // 开挖掘机,调用基类被隐藏的成员函数return 0;
}

1.2 构造函数

1.2.1 派生类与基类构造函数的和关系

        构造函数与析构函数不能被继承

include <iostream>using namespace std;// 基类
class Father
{
private:string name;
public:Father(string name):name(name){}string get_name(){return name;}
};// 派生类
class Son:public Father
{
public:};int main()
{
//    Son son;  // 找不到基类的无参构造函数
//    Son son("张"); // 没有派生类的有参构造函数return 0;
}

1.2.2 解决方案
1.2.2.1 补充基类的无参构造函数
#include <iostream>using namespace std;// 基类
class Father
{
private:string name;
public:Father(){name = "张";}Father(string name):name(name){}string get_name(){return name;}
};// 派生类
class Son:public Father
{
public:};int main()
{Son son;cout << son.get_name() << endl;
//    Son son("张"); // 没有派生类的有参构造函数return 0;
}
1.2.2.2 手动在派生类中调用基类构造函数
1.2.2.2.1 透传构造        
#include <iostream>using namespace std;// 基类
class Father
{
private:string name;
public:Father(){name = "张";}Father(string name):name(name){}string get_name(){return name;}
};// 派生类
class Son:public Father
{
public:// 编译器会自动添加构造函数。透传构造
//    Son():Father(){}// 手动添加构造函数。透传构造Son():Father("王"){}Son(string fn):Father(fn){}};int main()
{Son son;cout << son.get_name() << endl; // 王Son son1("张");cout << son1.get_name() << endl; // 张return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/417632.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA 安装,激活,使用,常用插件

1. 下载安装&#xff0c; 自行下载 2.打开到这步立马退出 3.使用工具 点击工具 等待几秒 查看效果。 恭喜你&#xff0c;成功&#xff01;&#xff01;&#xff01;&#xff01; 恭喜你&#xff0c;成功&#xff01;&#xff01;&#xff01;&#xff01; 恭喜你&#xff0…

移动硬盘显示需要格式化怎么办?教你快速应对

在日常使用电脑的过程中&#xff0c;移动硬盘因其便携性和大容量存储的特点&#xff0c;成为了许多用户备份和传输数据的重要工具。 然而&#xff0c;有时当我们连接移动硬盘到电脑时&#xff0c;可能会遇到一个令人头疼的问题——系统提示“移动硬盘需要格式化”。面对这种情…

ZPC显控一体机,精彩不止一面!

显控一体机的应用&#xff0c;有很多场景会遇到自带显示屏固定不灵活、尺寸不够大等问题。扩展屏幕便是一个很好的解决方案&#xff01;本文将带您解锁ZPC显控一体机的“多面精彩”。 ZPC简介 ZPC系列显控一体机 是广州致远电子全新研发的集“显示”“控制”一体化的高性能显控…

使用pytorch深度学习框架搭建神经网络

简介 现在主流有两个框架pytorch和TensorFlow,本文主要介绍pytorch PyTorch&#xff1a;由 Facebook 的人工智能研究小组开发和维护。PyTorch 以其动态计算图&#xff08;Dynamic Computational Graph&#xff09;和易用性著称&#xff0c;非常适合研究人员和开发者进行实验和…

在SOLIDWORKS中高效转换:从实体模型到钣金件的设计优化

在设计生产中&#xff0c;当我们收到中间格式的模型文件时&#xff0c;并希望将其转换为钣金件以进一步加工生产&#xff0c;该怎么做呢&#xff1f; 利用SOLIDWORKS软件&#xff0c;可以直接将实体模型转换为钣金件&#xff0c;来完成后续的设计。 中性文件 钣金件 一、设置…

密钥分发与公钥认证:保障网络通信的安全

在网络通信中&#xff0c;密钥的安全分发和公钥的有效认证是确保系统安全的关键。本文将为基础小白介绍密钥分发与公钥认证的基本概念和实际应用&#xff0c;帮助大家更好地理解这些技术如何保障我们的网络通信安全。 1. 密钥分发与公钥认证的背景 由于密码算法是公开的&…

_get_gt_mask、cat_mask、_get_other_mask

import torch# 定义获取标签掩码的函数 def _get_gt_mask(logits, target):print("原始 logits:\n", logits)print("目标 target:\n", target)# 将 target 拉平为一维张量target target.reshape(-1)print("拉平后的 target:\n", target)# 创建一…

C端产品如何转行成为大模型产品经理?

1、能力优劣势 C端产品经理的优势在于对用户需求、用户体验、数据分析、市场竞争等方面有较深的理解和实践&#xff0c;能够从用户视角出发&#xff0c;设计出吸引和留住用户的产品功能和交互。 C端产品经理的劣势在于对大模型的技术原理、应用场景、生态建设等方面缺乏足够的…

探伤仪的介绍

探伤仪就是一个高级测厚仪而已。 也有人说探伤仪功能那么强大&#xff0c;怎么说实质上就是一个测厚仪呢&#xff0c; 大家想想看&#xff0c;所谓探伤&#xff0c;最基本的要求就是测出工件内部缺陷的位置&#xff0c;这不就是测厚功能吗&#xff0c; 当然除了测厚&#xf…

pyro ExponentialLR 如何设置优化器 optimizer的学习率 pytorch 深度神经网络 bnn,

第一。pyro 不支持 “ReduceLROnPlateau” &#xff0c;因为需要Loss作为输入数值&#xff0c;计算量大 pytorch的学习率调整 视频 看这个博主的视频 05-01-学习率调整策略_哔哩哔哩_bilibili 第二 &#xff0c;svi 支持 scheduler注意点&#xff0c; 属于 pyro.optim.PyroOp…

从0到1深入理解vite

一、什么是构建工具 ts:如果遇到ts文件&#xff0c;我们需要使用tsc把ts转换为jsreact/vue &#xff1a; 安装react-compiler、vue-conplier 将我们写的jsx或者vue文件转换成render函数less/sass/postcss/somponent-style:我们又需要less-loader、sass-loader等一系列编译工具…

目标检测-RT-DETR

RT-DETR (Real-Time Detection Transformer) 是一种结合了 Transformer 和实时目标检测的创新模型架构。它旨在解决现有目标检测模型在速度和精度之间的权衡问题&#xff0c;通过引入高效的 Transformer 模块和优化的检测头&#xff0c;提升了模型的实时性和准确性。RT-DETR 可…

Ceph RBD使用

CephRBD使用 一、RBD架构说明二、RBD相关操作1、创建存储池2、创建img镜像2.1 创建镜像2.1.2 查看镜像详细信息2.1.3 镜像其他特性2.1.4 镜像特性的启用和禁用 3、配置客户端使用RBD3.1 客户端配置yum源3.2 客户端使用admin用户挂载并使用RBD3.2.1 同步admin账号认证文件3.2.2 …

社交媒体的智能变革:Facebook AI优化用户体验

Facebook作为全球领先的社交平台&#xff0c;一直致力于通过人工智能&#xff08;AI&#xff09;技术提升用户体验。AI技术在Facebook的应用涵盖了推荐系统、自然语言处理、广告投放和用户反馈等多个方面&#xff0c;使平台的互动和内容体验更加智能和个性化。 推荐系统的智能化…

结构型设计模式—外观模式

结构型设计模式—外观模式 在软件开发的过程中&#xff0c;你是否遇到过这样的情况&#xff1a;你需要调用一个复杂系统中的多个模块&#xff0c;而每个模块都有自己的接口和使用方法&#xff0c;这让你不得不面对复杂的调用逻辑和大量的冗余代码&#xff1f;这时候&#xff0…

【网络安全】XSS+OTP绕过+账户接管

未经许可,不得转载。 文章目录 正文XSSOTP绕过账户接管正文 目标:www.example.com XSS 不断寻找可能存在XSS的点位。 终于,在个人资料页面:www.example.com/profile_details.php?userid= ,使用Payload<script>alert(1)</script>,实现XSS: 因此,能够实…

vxe-table——实现table 动态显示 +冻结列等功能——技能提升

之前我也有写过类似的功能&#xff0c;就是可以自定义勾选需要展示的列。 不过之前是我自己写的弹窗处理的&#xff0c;有现成的插件vex-table插件可以使用。 vxe-table官网&#xff1a;https://vxetable.cn/v3/#/table/api 解决步骤1&#xff1a;安装vxe-table——npm inst…

HTTP状态码介绍,带你了解请求响应全过程

1xx状态码&#xff1a;&#x1f449;表示信息响应&#xff0c;客户端请求已被接收&#xff0c;继续处理。 100 - Continue&#xff1a;客户端应继续其请求。&#x1f914; 101 -Switching Protocols&#xff1a;服务器已经理解并接受了客户端的请求&#xff0c;将切换协议。 10…

【自用14】C++俄罗斯方块-思路复盘

1.编写主函数 int main(void){welcome();//欢迎函数system("pause");//窗口停留colsegraph();//关闭图画return 0;//返回值 }其中包含有最开始的欢迎&#xff0c;以及基础的窗口停留、图画关闭和返回值语句 2.编写欢迎函数 需求&#xff1a; 欢迎函数中需要包含的…

Java如何读取resources目录下的文件路径(九种代码示例教程)

本文摘要&#xff1a;Java如何读取resources目录下的文件路径 &#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。公粽号&#xff1a;洲与AI。 &#x1f91…