C++笔记

C++笔记

C++ 开源库:http://ffmpeg.club/cppsdk_download.html

C 和 C++ 参考手册:http://www.cppds.com/

课程:https://jiedi.ke.qq.com/

https://www.learncpp.com/ 、 中文翻译:https://learncpp-cn.github.io/(只翻译了几个)

官方文档:https://zh.cppreference.com/w/

c语言中文网:http://c.biancheng.net/

菜鸟教程:https://www.runoob.com/cplusplus/cpp-tutorial.html

c++学习的书籍要看 c++ primer

C++中文网:https://c-cpp.com/

C++引用传递

chatgpt

引用传递是 C++ 中的一种传参方式,是在函数调用时通过引用(&)的形式将实际参数的地址传给形式参数,这样在函数内操作形式参数就相当于操作实际参数,也就是说形式参数和实际参数共用同一内存空间,任何改变形式参数的值都会影响到实际参数的值。

C++ static关键字

chatgpt

分为static参数(变量)、static函数、static类成员。

在 C++ 中,static 是一种关键字,它可以应用于变量、函数和类成员,具有不同的含义。

  1. static 变量:

static 变量是一种特殊的变量,具有静态存储期和块作用域。这意味着该变量在整个程序执行期间都存在,并且只能在定义该变量的块内访问。static 变量的初始值在程序开始执行时就被初始化了,并且只初始化一次,即使该变量在多个函数中被使用。

  1. static 函数:

static 函数是一种只能在定义该函数的文件中使用的函数,即它具有内部链接(internal linkage)。这种函数不能在其他文件中被调用,因为它们不会被放在程序的全局符号表中。通常,static 函数在文件中定义的目的是为了将其私有化,只能在该文件中使用,防止它被其他文件误用。

  1. static 类成员:

static 类成员是类的成员变量或成员函数,它们属于整个类,而不是类的实例。它们可以被类的所有实例共享,并且只有一个副本。static 类成员在程序开始执行时被初始化,并且只初始化一次。static 类成员可以通过类名和作用域解析运算符 :: 来访问,而不需要通过类的实例来访问。

总之,static 关键字的用途是在 C++ 中定义静态变量、函数和类成员,并且这些实体具有特殊的作用域、存储方式和链接属性。

静态函数

C++ 中的静态函数是指那些不需要对象就可以调用的函数,并且在程序的整个生命周期内仅存在一个实例。静态函数通常是作为类的工具函数实现,不需要访问类的非静态成员,因此它们不需要类的对象实例。静态函数可以在类内部或者类外部定义。

静态函数的声明方式为:

1
2
3
static return-type function-name(arguments) { 
// function body
}

或者

1
2
3
return-type class-name::function-name(arguments) { 
// function body
}

静态参数

在 C++ 中,函数内的静态参数是指在函数内部声明的静态变量。这个变量的生命周期是整个程序运行期间,并且在函数多次调用时,其值不会被重置。因此,静态参数在函数内部可以记录和保存信息,并且在下一次调用该函数时可以使用这些信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

void printCounter(static int counter)
{
counter++;
std::cout << "Counter: " << counter << std::endl;
}

int main()
{
int counter = 0;
printCounter(counter);
printCounter(counter);
printCounter(counter);
return 0;
}

输出:

1
2
3
Counter: 1
Counter: 2
Counter: 3

这里,静态参数counter在多次调用函数printCounter时保持了其值,并递增。

静态类成员

C++中static类成员举例

在C++中,static关键字可以用来修饰类的成员变量或成员函数,它表示这些成员属于整个类,而不是属于某个具体的对象。下面是一个示例,演示如何在类中使用静态成员变量:

用static修饰的话,就可以直接用 类::类成员 进行访问

初始化要在main函数外部初始化!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyClass {
public:
static int count; // 静态成员变量 count,用来记录创建的 MyClass 对象的数量

MyClass() { // 构造函数
count++; // 每次创建对象时,count 自增 1
}

~MyClass() { // 析构函数
count--; // 每次销毁对象时,count 自减 1
}
};

int MyClass::count = 0; // 静态成员变量 count 的定义和初始化

int main() {
MyClass obj1; // 创建 MyClass 对象 obj1,count 自增 1
MyClass obj2; // 创建 MyClass 对象 obj2,count 自增 1

cout << "The number of MyClass objects: " << MyClass::count << endl; // 输出 MyClass 对象的数量
// 输出结果:The number of MyClass objects: 2

return 0;
}

在上面的代码中,我们定义了一个类 MyClass,其中包含一个静态成员变量 count,它用来记录创建的 MyClass 对象的数量。在类的构造函数和析构函数中,我们对 count 进行自增和自减操作。在程序的主函数中,我们创建了两个 MyClass 对象,然后通过 MyClass::count 来访问静态成员变量 count,输出 MyClass 对象的数量。

需要注意的是,静态成员变量的定义和初始化必须在类的外部进行,例如 MyClass::count = 0。而且,在使用静态成员变量时,可以通过类名和作用域解析运算符(::)来访问,例如 MyClass::count。

linux怎么单独编译C++头文件

chatgpt

要单独编译一个 C++ 头文件,你需要创建一个与头文件同名的 C++ 源文件(通常是 .cpp 文件),然后将这个源文件与该头文件一起编译即可。

假设你有一个名为 foo.h 的头文件,那么你可以创建一个名为 foo.cpp 的源文件,并将其与 foo.h 一起编译,命令如下:

1
g++ -c foo.cpp -o foo.o

其中,-c 表示只编译,不链接;-o foo.o 表示将编译结果保存为 foo.o 文件。

编译完成后,你可以将 foo.o 文件链接到你的程序中,以使用其中定义的函数和变量,命令如下:

1
g++ main.cpp foo.o -o main

其中,main.cpp 是你的主程序源文件,-o main 表示将链接结果保存为 main 可执行文件。

这样,你就可以在主程序中使用 foo.h 中定义的函数和变量了。

Linux下编译C/C++项目指定头文件和链接库搜索路径

Linux下编译C/C++项目指定头文件和链接库搜索路径

头文件路径

  • 方式1:#include <…>
  • 方式2:#include “…”

<说明> #include <…> 和 #include “…” 的区别:

  1. 前者一般会优先搜索 -I 选项后的头文件路径(即用gcc编译时的-I选项),之后就是标准的系统头文件路径。
  2. 后者是从当前的目录来搜索
1
2
#示例,会优先从/home/xiaowp/include路径去搜索#include <xxx.h>的头文件
gcc foo.c -I/home/xiaowp/include -o foo
  • 方式3:在gcc命令或者Makefile文件中,使用 -I 来设置头文件的路径

  • 方式4:设置环境变量。

  1. 在当前用户目录下,设置环境变量,只针对当前用户有效。

    vim .bash_profile 或者 .bashrc:

    export C_INCLUDE_PATH=$C_INCLUDE_PATH:头文件路径 #C语言

    export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:头文件路径 #C++

  2. 设置全局环境变量,对所有用户生效,需要root权限。vim /etc/profile

    <注意> 如果当前用户设置了同样的环境变量,那么将屏蔽掉相同名称的全局环境变量的作用域。

Linux系统标准头文件路径

/usr/include

/usr/local/include

可以使用 cpp -v 命令来查看标准系统头文件的路径。

new 和 delete

内存泄露。是因为有一块内存申请了没释放,这块内存之后无人问津,不同地方多次申请内存后,把内存榨干,无内存可用了。

delete p; 不是删除p,而是释放p指向的内存。

举例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13

void func(){
int a = 100; // a的内存是由编译器自动分配的,函数运行结束时,编译器会自动释放内存
int *pp = &a; // p存储了a的地址

int *p = new int; // 动态请求一个int大小的内存
delete p; // 注意,这里不是删除p,而是释放p指向的内存。
p = &a; // 删除之后,还可以把p指向别的地方。

int *ppp = new int[100];
delete[] ppp;

}

存在一种情况,加delete也可能会造成内存泄漏

1
2
3
4
5
void some_func(){
Object* p = new Object;
p->foo();
delete p;
}

如果 p->foo(); 这一步抛出了异常,那么delete这句话可能不会执行。

在哪里释放内存delete掉呢?是这个func()函数里吗,如果外面还有引用呢?一旦数据的使用传递变得复杂,就可能会忘记释放内存。

看汇编

在visual studio里,菜单栏 调试 –> 窗口 –> 反汇编

C++不允许嵌套定义

C++不允许函数嵌套定义!(或C++不允许在main函数定义中定义函数)

  1. 函数内部不能再新定义一个函数,但是可以用新一个函数。
  2. 函数不能嵌套定义,但可以在函数中声明函数,在函数外定义函数;
  3. 在函数中声明的函数的作用域不是它所在的整个函数;
  4. 如果能不在函数中声明函数,就别这样做,这样实现起来逻辑复杂容易出错。

C++用类成员函数必须实例化一个类对象

举例:

a.cc:

1
2
3
4
5
6
#include "a.h"
#include <iostream>

void a::printa(){
std::cout << "a" << std::endl;
}

a.h:

1
2
3
4
class a{
public:
void printa();
};

b.cc:

1
2
3
4
5
6
7
#include "a.h"
#include <iostream>
int main(){
a A;
A.printa(); //必须要实例化,才能调用类函数
return 0;
}

命令行敲 g++ b.cc a.cc -o b

C++虚函数

  • 虚函数用于替换基类提供的实现。 只要有问题的对象实际上是派生类的,总是调用替换,即使该对象是通过基指针而不是派生指针访问的。
  • 虚函数是存在于基类中并由派生类重新定义的成员函数。
  • 当在基类和派生类中使用相同的函数名时,基类中的函数使用关键字 virtual 声明。
  • 当函数变为虚函数时,C++ 会在运行时根据基类指针指向的对象的类型来确定调用哪个函数。 因此,通过使基类指针指向不同的对象,我们可以执行不同版本的虚函数。

虚函数规则:

  • 虚函数应该是某个类的成员。
  • 虚函数不能是静态成员。
  • 使用对象指针调用虚函数。
  • 它可以是另一个班级的朋友。
  • C++ 不包含虚拟构造函数,但可以具有虚拟析构函数。

chatgpt

C++中,虚函数是一种特殊的成员函数,可以被子类继承并重写,使得在运行时根据实际的对象类型动态调用相应的函数。虚函数的实现通过在函数声明前加上 virtual 关键字来实现。

使用虚函数可以实现多态,即让子类对象可以用父类指针或引用进行操作,并且能够在运行时自动调用相应的子类函数。在有继承关系的类中,如果一个函数是虚函数,当通过父类指针或引用调用该函数时,实际上会调用相应子类的函数。这种机制可以避免手动进行类型转换,增加了程序的灵活性和可维护性。