C++必知必会之基础知识-常用关键字(1)

2023-09-15 08:41:46 浏览数 (1)

START

温故而知新

Hello,大家好!我是木荣。温故而知新,可以为师矣。作为一名攻城狮,扎实的基本功是解决问题及完成工作中任务的重要前提。没有良好的基本功作为铺垫,一味的追求知识的宽度是毫无意义,知其然更要知其所以然。因此,在平时和小伙伴们聊天时,在谈到学习技术方面的问题,我会告诉他们注重基本功。所以,最近文章会总结一些日常编程工作中常用的重要基本知识点,根据平时工作中常用的也是重要的知识点逐步展开。

为了提高阅读的舒适性,不会像其他博主的什么万字长文,一篇讲解完成,那样即浪费时间,篇幅过长记住的知识点也不会很多。所以,所有知识点会分多章多节发布,每篇尽量让大家短时间读完而尽可能记住知识点,希望喜欢的小伙伴们加关注呦!

常用关键字

C/C 首先我们先来了解一下修饰符的定义。

修饰符在C/C 中,修饰符(modifiers)是用于修改基本数据类型的关键字,用于改变变量的存储方式、作用域或其他特性。

const

const关键字是一种修饰符。就 const 修饰符而言,它用来告诉编译器,被修饰的这些部分的特点具有只读属性。在编译的过程中,一旦编写代码试图去改变这些部分,编译器就会给出错误提示。防止编程中出现语法及逻辑的错误,提高代码的健壮性及规范性。

  • 修饰变量说明该变量不可以被改变
  • 修饰指针分为指向常量的指针和指针常量
  • 修饰引用常量引用,经常用于形参类型,即避免了拷贝,又避免了函数对值的修改
  • 修饰成员函数说明该成员函数内不能修改成员变量
代码语言:javascript复制
//类
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关键字声明的变量是静态变量,它在程序的生命周期内保持其值,不会在每次函数调用时重新初始化。静态变量存储在静态数据区,而不是栈上。当一个静态变量在函数内部声明时,它会在程序运行时初始化并保留其值。

代码语言:javascript复制
#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关键字在类中声明的成员函数被称为静态函数,也称为类的静态成员函数。静态函数与类的实例无关,可以直接通过类名调用,而无需通过对象。

代码语言:javascript复制
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关键字声明的数据成员被称为静态数据成员,它属于类本身,而不是类的实例。静态数据成员在所有类的实例之间共享,类的所有对象共享同一个静态数据成员。

代码语言:javascript复制
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关键字声明的全局变量(位于函数外部)具有文件作用域,它们只在声明它们的文件中可见,不会被其他文件访问。这些静态变量不能被其他文件直接访问,因此在不同文件中使用相同名称的静态变量不会造成命名冲突。

代码语言:javascript复制
// 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指针的用法:

代码语言:javascript复制
#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关键字。例如:

代码语言:javascript复制
// 在类定义中声明内联函数
class MyClass {
public:
    inline void foo();
};

// 在函数定义时声明内联函数
inline void MyClass::foo() {
    // 函数代码
}
  • 编译器决策:inline关键字只是向编译器发出了一个请求,请求将函数内容内联到调用点。编译器会自行决定是否真正内联展开函数代码,它可能会考虑函数的复杂性、调用频率等因素来作出最优的决策。
  • 适用场景:内联函数对于短小且频繁调用的函数效果最好,而对于复杂的函数或大量逻辑的函数可能并不适合内联。适当地使用内联函数可以提高性能,但滥用内联可能会导致代码膨胀,增加可执行文件的大小。
  • 定义位置:通常将内联函数的定义放在头文件中,因为在每个调用点都需要展开函数代码,编译器需要知道函数的实现细节。
  • 不支持递归:内联函数不支持递归调用,因为递归调用无法在调用点展开。
  • 静态成员:类中的静态成员函数默认是内联的,即使没有显式使用inline关键字。

使用内联函数的示例:

代码语言:javascript复制
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函数的代码,从而减少函数调用的开销。

需要注意的是,虽然内联函数可以提高性能,但并不是所有的函数都适合内联。适当地使用内联函数是一种优化手段,应该根据实际情况和性能测试来决定是否使用内联。

好文推荐

0 人点赞