温故而知新
Hello,大家好!我是木荣。温故而知新,可以为师矣。作为一名攻城狮,扎实的基本功是解决问题及完成工作中任务的重要前提。没有良好的基本功作为铺垫,一味的追求知识的宽度是毫无意义,知其然更要知其所以然。因此,在平时和小伙伴们聊天时,在谈到学习技术方面的问题,我会告诉他们注重基本功。所以,最近文章会总结一些日常编程工作中常用的重要基本知识点,根据平时工作中常用的也是重要的知识点逐步展开。
为了提高阅读的舒适性,不会像其他博主的什么万字长文,一篇讲解完成,那样即浪费时间,篇幅过长记住的知识点也不会很多。所以,所有知识点会分多章多节发布,每篇尽量让大家短时间读完而尽可能记住知识点,希望喜欢的小伙伴们加关注呦!
常用关键字
C/C++首先我们先来了解一下修饰符
的定义。
修饰符在C/C++中,修饰符(modifiers)是用于修改基本数据类型的关键字,用于改变变量的存储方式、作用域或其他特性。
const
const关键字是一种修饰符。就 const 修饰符而言,它用来告诉编译器,被修饰的这些部分的特点具有只读属性。在编译的过程中,一旦编写代码试图去改变这些部分,编译器就会给出错误提示。防止编程中出现语法及逻辑的错误,提高代码的健壮性及规范性。
-
修饰变量说明该变量不可以被改变
-
修饰指针分为指向常量的指针和指针常量
-
修饰引用常量引用,经常用于形参类型,即避免了拷贝,又避免了函数对值的修改
-
修饰成员函数说明该成员函数内不能修改成员变量
//类
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;
}
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关键字的具体含义可能会因上下文而异,因此应根据具体情况理解和使用。
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++中是一个非常有用的特性,它使得在成员函数中能够轻松访问当前对象的成员,并提供了便捷的方式来实现方法链式调用。
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函数的代码,从而减少函数调用的开销。
需要注意的是,虽然内联函数可以提高性能,但并不是所有的函数都适合内联。适当地使用内联函数是一种优化手段,应该根据实际情况和性能测试来决定是否使用内联。