大家好,我是Linux兵工厂,在工作经常发现小伙伴们遇到一些C++的问题都是对基础知识不熟悉或理解混乱所导致的。正所谓万丈高楼平地起,作为一名合格的程序员来说,没有良好的基本功很难达到一定的高度。而工作中大部分编程问题都是基本功不扎实所导致,所以决定花些时间来整理C++相关的基本知识和基本概念供大家参考理解,每一个知识点都结合相关的代码进行验证。本文基本上涵盖了C++最常用的知识点,希望对小伙伴们有所帮助。关注公众号:Linux兵工厂,领取海量Linux免费学习资料,且会不定时输出更多干货知识。
C/C++首先我们先来了解一下修饰符的定义。
修饰符在C/C++中,修饰符(modifiers)是用于修改基本数据类型的关键字,用于改变变量的存储方式、作用域或其他特性。
1. const
const关键字是一种修饰符。就 const 修饰符而言,它用来告诉编译器,被修饰的这些部分的特点具有只读属性。在编译的过程中,一旦编写代码试图去改变这些部分,编译器就会给出错误提示。防止编程中出现语法及逻辑的错误,提高代码的健壮性及规范性。
- 修饰变量
说明该变量不可以被改变0
- 修饰指针
分为指向常量的指针和指针常量
- 修饰引用
常量引用,经常用于形参类型,即避免了拷贝,又避免了函数对值的修改
- 修饰成员函数
说明该成员函数内不能修改成员变量
//类
class Test
{
public:
Test() : a(0) { }; // 初始化参数列表
Test(int x) : a(x) { }; // 初始化参数列表
int getV(); // 普通成员函数
int getV() const; // 常成员函数,不得修改类中的任何数据成员的值
private:
const int a; // 常对象成员,只能在初始化列表赋值
};
void Func()
{
Test b; // 普通对象,可以调用全部成员函数
const Test a; // 常对象,只能调用常成员函数,修改常成员变量
const Test *p = &a; // 常指针
const Test &q = a; // 常引用
// 如果const位于星号*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
// 如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。
const int* a; // 常量指针 指针指向的常量不可修改
int const *a; // 常量指针 指针指向的常量不可修改(同上)
int* const a; // 指针常量 指针不可修改
const int* const; // 常量指针常量 指针和指向的常量都不可修改
// 引用 引用a不能被修改
int x;
int const &a=x;
const int &a=x;
}
2. static
static关键字常用于修饰变量和函数
- 修饰普通变量
修改变量的存储区域和生命周期,使变量存储在静态存储区,在 main函数运行前就分配了空间,如果有初始值就用初始值初始化它,如果没有初始值系统用默认值初始化它。
- 修饰普通函数
表明函数的作用范围,仅在定义该函数的文件内才能使用。在多人开发项目时,为了防止与他人命名空间里的函数重名,可以将函数定位为 static。
- 修饰成员变量
修饰成员变量使所有的对象只保存一个该变量,而且不需要生成对象就可以访问该成员。
- 修饰成员函数
修饰成员函数使得不需要生成对象就可以访问该函数,但是在 static 函数内不能访问非静态成员。
在函数内部使用static关键字声明的变量是静态变量,它在程序的生命周期内保持其值,不会在每次函数调用时重新初始化。静态变量存储在静态数据区,而不是栈上。当一个静态变量在函数内部声明时,它会在程序运行时初始化并保留其值。
#include <iostream>
void functionWithStatic() {
static int count = 0; // 静态变量,在多次调用该函数时,count的值保留
count++;
std::cout << "Static count: " << count << std::endl;
}
int main() {
functionWithStatic(); // 输出 Static count: 1
functionWithStatic(); // 输出 Static count: 2
functionWithStatic(); // 输出 Static count: 3
return 0;
}
在C/C++中,使用static关键字在类中声明的成员函数被称为静态函数,也称为类的静态成员函数。静态函数与类的实例无关,可以直接通过类名调用,而无需通过对象。
class MyClass {
public:
static void staticFunction() {
std::cout << "This is a static function." << std::endl;
}
};
int main() {
MyClass::staticFunction(); // 直接通过类名调用静态函数
MyClass obj;
obj.staticFunction(); // 也可以通过对象调用静态函数,但不是推荐做法
return 0;
}
在类中使用static关键字声明的数据成员被称为静态数据成员,它属于类本身,而不是类的实例。静态数据成员在所有类的实例之间共享,类的所有对象共享同一个静态数据成员。
class MyClass {
public:
static int staticData; // 静态数据成员的声明
};
int MyClass::staticData = 42; // 静态数据成员的定义和初始化
int main() {
MyClass obj1;
MyClass obj2;
obj1.staticData = 10;
std::cout << "obj1.staticData: " << obj1.staticData << std::endl; // 输出 obj1.staticData: 10
std::cout << "obj2.staticData: " << obj2.staticData << std::endl; // 输出 obj2.staticData: 10
return 0;
}
在文件中使用static关键字声明的全局变量(位于函数外部)具有文件作用域,它们只在声明它们的文件中可见,不会被其他文件访问。这些静态变量不能被其他文件直接访问,因此在不同文件中使用相同名称的静态变量不会造成命名冲突。
// File1.cpp
static int file1StaticVar = 10;
// File2.cpp
static int file2StaticVar = 20;
以上是static关键字在C/C++中的常见用法。请注意,使用static关键字的具体含义可能会因上下文而异,因此应根据具体情况理解和使用。
3. this指针
在C++中,this指针是一个特殊的指针,它是一个隐藏的指针,指向当前对象(即正在调用该成员函数的对象)。this指针在成员函数内部自动创建,可以在成员函数中使用,用于访问当前对象的成员变量和成员函数。
this指针是一个隐式参数,它并不需要显式地传递,编译器会在调用成员函数时自动传递它。
以下是关于this指针的一些详细解释:
- this指针的类型:
this指针的类型是指向当前类对象的指针,它的类型是指向当前类的常量指针(const指针)。这是因为在成员函数中,不能通过this指针来修改当前对象的值,以保证成员函数的const属性能够得到维持。
- this指针的用途:
在成员函数中,使用this->可以访问当前对象的成员变量和成员函数,以区分成员变量和函数参数的命名冲突。
在类的静态成员函数中,没有this指针,因为静态成员函数不依赖于特定的对象。
- this指针的使用场景:
当成员函数中的参数和成员变量同名时,使用this指针可以明确指示成员变量。
在类的方法链式调用中,返回this指针可以使调用更加简洁。
下面是一个示例代码,演示了this指针的用法:
#include <iostream>
class MyClass {
public:
int x;
MyClass(int x) : x(x) {}
void printX() {
std::cout << "x = " << this->x << std::endl;
}
MyClass& increment() {
this->x++;
return *this;
}
};
int main() {
MyClass obj(10);
obj.printX(); // 输出 x = 10
obj.increment().increment().increment();
obj.printX(); // 输出 x = 13
return 0;
}
在上述示例中,this指针用于访问成员变量x,并在方法链式调用中返回了当前对象的引用。这样可以连续调用increment()函数,并对成员变量x进行递增操作。
总之,this指针在C++中是一个非常有用的特性,它使得在成员函数中能够轻松访问当前对象的成员,并提供了便捷的方式来实现方法链式调用。
4. inline内联函数
在C++中,inline是一个关键字,用于对函数进行内联展开。使用inline关键字声明的函数被称为内联函数。内联函数的主要目的是减少函数调用的开销,通过在函数调用点展开函数代码,可以避免函数调用的额外开销,从而提高程序的执行效率。
以下是内联函数的一些特点和注意事项:
定义:内联函数通常在类定义中声明,也可以在函数定义时加上inline关键字。例如:
// 在类定义中声明内联函数
class MyClass {
public:
inline void foo();
};
// 在函数定义时声明内联函数
inline void MyClass::foo() {
// 函数代码
}
- 编译器决策:inline关键字只是向编译器发出了一个请求,请求将函数内容内联到调用点。编译器会自行决定是否真正内联展开函数代码,它可能会考虑函数的复杂性、调用频率等因素来作出最优的决策。
- 适用场景:内联函数对于短小且频繁调用的函数效果最好,而对于复杂的函数或大量逻辑的函数可能并不适合内联。适当地使用内联函数可以提高性能,但滥用内联可能会导致代码膨胀,增加可执行文件的大小。
- 定义位置:通常将内联函数的定义放在头文件中,因为在每个调用点都需要展开函数代码,编译器需要知道函数的实现细节。
- 不支持递归:内联函数不支持递归调用,因为递归调用无法在调用点展开。
- 静态成员:类中的静态成员函数默认是内联的,即使没有显式使用inline关键字。
使用内联函数的示例:
class MathUtil {
public:
inline static int add(int a, int b) {
return a + b;
}
};
int main() {
int result = MathUtil::add(5, 3);
return 0;
}
在上述示例中,add函数被声明为内联静态函数。在调用MathUtil::add(5, 3)时,编译器会尝试在调用点展开add函数的代码,从而减少函数调用的开销。
需要注意的是,虽然内联函数可以提高性能,但并不是所有的函数都适合内联。适当地使用内联函数是一种优化手段,应该根据实际情况和性能测试来决定是否使用内联。