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

C/C++
181
0
0
2024-02-11

START

位域

在C++中,位域(bit fields)是一种特殊的数据结构,允许将结构体或类的成员变量按位进行分配。通过位域,可以有效地利用内存,节省存储空间,特别适用于表示布尔类型、标志位或其他不需要完整字节的数据。

位域的语法格式如下:

struct MyStruct 
{
    dataType memberName : numBits;
};

其中,dataType是要存储的数据类型,memberName是位域成员的名称,numBits是分配给该成员的位数。numBits表示该成员所占用的位数,必须是正整数,不能超过数据类型的位数。

以下是一个简单的例子,演示了C++类中位域的使用:

#include <iostream>

class MyFlags {
public:
    MyFlags() : isRed(0), isGreen(0), isBlue(0) {}

    // 位域成员
    unsigned int isRed : 1;
    unsigned int isGreen : 1;
    unsigned int isBlue : 1;
};

int main() {
    MyFlags myFlags;
    myFlags.isRed = 1;
    myFlags.isGreen = 0;
    myFlags.isBlue = 1;

    std::cout << "Size of MyFlags: " << sizeof(MyFlags) << " bytes" << std::endl;
    std::cout << "isRed: " << myFlags.isRed << std::endl;
    std::cout << "isGreen: " << myFlags.isGreen << std::endl;
    std::cout << "isBlue: " << myFlags.isBlue << std::endl;

    return 0;
}

输出可能为:

Size of MyFlags: 4 bytes
isRed: 1
isGreen: 0
isBlue: 1

在上述示例中,我们定义了一个名为MyFlags的类,其中包含三个位域成员isRed、isGreen和isBlue,每个成员都占用1位。由于unsigned int通常是4字节(32位),所以类MyFlags的大小为4字节。

在使用类中的位域时,需要注意类的成员访问权限以及可能的内存对齐问题。位域成员只能是整数类型或枚举类型,并且不支持引用。类中的位域成员也受到相同的限制,不能超过其数据类型的位数。

使用位域时应该谨慎考虑,确保了解位域的特性和限制,并在适当的情况下使用它们,以提高内存利用效率。在需要移植性和可靠性的场景中,建议使用常规的数据成员而不是位域

extern “C”

在C++中,extern "C"是一个用于声明C语言风格的函数和变量的关键字。C++与C在编译和链接过程中有一些差异,其中包括名称修饰(name mangling)和函数重载等特性。使用extern "C"可以告诉C++编译器将某些函数和变量按照C语言的规则进行处理,以实现C和C++之间的混合编程。

使用extern "C"有以下几个常见的场景:

  • C++调用C语言库:当C++代码需要调用一个由C语言编写的库时,由于C和C++之间的名称修饰不同,需要使用extern "C"来正确链接C语言的函数。
  • C语言调用C++函数:当C语言代码需要调用一个由C++编写的函数时,由于C++可能存在函数重载和其他特性,需要使用extern "C"来告诉C语言编译器按照C语言的方式处理函数。

以下是一些示例,说明了extern "C"的用法:

C++调用C语言库的示例:C++代码(main.cpp):

#include <iostream>

extern "C" 
{
    void c_function(); // 声明C语言风格的函数
}

int main() 
{
    c_function(); // 调用C语言风格的函数
    return 0;
}

C语言代码(c_library.c):

#include <stdio.h>

void c_function() 
{
    printf("This is a C function.\n");
}

C语言调用C++函数的示例:

C++代码(cpp_library.cpp):

#include <iostream>

extern "C" 
{
    void cpp_function(); // 声明C语言风格的函数
}

void cpp_function() 
{
    std::cout << "This is a C++ function." << std::endl;
}

C语言代码(main.c):

extern void cpp_function(); // 声明C语言风格的函数

int main() 
{
    cpp_function(); // 调用C++函数
    return 0;
}

在上述示例中,我们通过使用extern "C"关键字来正确地链接C和C++之间的函数。

需要注意的是,extern "C"应该只用于C和C++之间的函数和全局变量的声明,而不应该用于类的定义和成员函数。因为类的成员函数涉及到C++的特性,无法通过简单的名称修饰解决链接问题。在需要使用C++类的情况下,可以考虑提供一个纯C接口来实现交互。

struct

在C++中,struct是用于定义自定义数据类型的关键字,它是一种用户定义的数据结构,可以包含不同类型的成员变量和成员函数。struct与class非常相似,但有一些不同之处。

以下是关于C++中struct的一些详解:

成员变量:struct可以包含不同类型的成员变量,这些成员变量默认是public(公共)访问权限的。这意味着结构体的成员可以从外部直接访问和修改。

struct MyStruct 
{
     int x;  // 公共成员变量,默认访问权限是 public
     double y; // 公共成员变量,默认访问权限是 public
};

成员函数:struct可以定义成员函数,用于操作和访问结构体的成员变量。

struct MyStruct 
{
    int x;

    void printX() 
    {
        std::cout << "x = " << x << std::endl;
    }
};

继承:struct可以通过继承派生出子结构体。派生类继承了基类的成员和方法。

 struct Base 
 {
     int x;
 };

 struct Derived : Base 
 {
     double y;
 };

构造函数和析构函数:struct可以定义构造函数和析构函数,用于对象的初始化和资源的清理。

struct MyStruct 
{
     int x;

     // 构造函数
     MyStruct(int value) 
     {
         x = value;
     }

     // 析构函数
     ~MyStruct() 
     {
         std::cout << "MyStruct object destroyed." << std::endl;
     }
 };

类型别名:struct可以使用typedef来定义类型别名。

struct MyStruct 
{
    typedef int MyInt; // 定义类型别名 MyInt
    MyInt x;
};

需要注意的是,尽管struct和class都可以用来定义自定义数据类型,但它们有一些细微的差别:

  • 在struct中,默认的成员访问权限是public,而在class中,默认的成员访问权限是private
  • 对于结构体,默认继承权限是public,而对于类,默认继承权限是private
  • 在语法上,类可以使用class关键字或struct关键字来定义,而struct只能用于定义结构体。
  • 除了默认的访问权限和默认继承权限之外,struct和class在其他方面几乎是相同的。使用哪个关键字取决于编程风格和设计选择。

union

在C++中,union是一种特殊的数据结构,允许在相同的内存位置存储不同的数据类型。union的所有成员共享相同的内存空间,这使得union在一些特定情况下非常有用,例如节省内存或进行类型转换。

union的语法如下:

union UnionName 
{
    dataType member1;
    dataType member2;
};

其中,UnionName是union的名称,dataType是要存储在union中的数据类型。union的成员可以是不同类型的变量,但是所有成员共享同一块内存,只有一个成员可以被赋值。在任何时候,union中只有一个成员的值是有效的,而其他成员的值将是未定义的。

以下是一个简单的示例,演示了union的用法:

#include <iostream>

union MyUnion 
{
    int intValue;
    double doubleValue;
    char charValue;
};

int main() 
{
    MyUnion u;

    u.intValue = 42;
    std::cout << "intValue: " << u.intValue << std::endl;

    u.doubleValue = 3.14;
    std::cout << "doubleValue: " << u.doubleValue << std::endl;

    u.charValue = 'A';
    std::cout << "charValue: " << u.charValue << std::endl;

    // 输出最后设置的成员值
    std::cout << "intValue after charValue assignment: " << u.intValue << std::endl;

    return 0;
}

输出可能为:

intValue: 42
doubleValue: 3.14
charValue: A
intValue after charValue assignment: 65

在上述示例中,我们定义了一个union MyUnion,它有三个成员intValue、doubleValue和charValue,分别是int、double和char类型。我们可以在不同的时间点给union的不同成员赋值。由于union的成员共享同一块内存,最后赋值的成员的值会覆盖之前的值。

C++中除此之外的特性还有:

  • 默认访问控制符为 public
  • 可以含有构造函数、析构函数
  • 不能含有引用类型的成员
  • 不能继承自其他类,不能作为基类
  • 不能含有虚函数
  • 匿名 union 在定义所在作用域可直接访问 union 成员
  • 匿名 union 不能包含 protected 成员或 private 成员
  • 全局匿名联合必须是静态(static)的
#include<iostream>
union UnionTest 
{
    UnionTest() : i(1) {};
    int i;
    double j;
};
static union 
{
    int i;
    double j;
};
int main() 
{
    UnionTest u;
    union 
    {
        int i;
        double j;
    };
    std::cout << u.i << std::endl; // 输出 UnionTest 联合的 1
    ::i = 2;
    std::cout << ::i << std::endl; // 输出全局静态匿名联合的 2
    i = 3;
    std::cout << i << std::endl; // 输出局部匿名联合的 3
    return 0;
}

需要特别注意的是,使用union需要非常小心,因为它的行为容易导致难以预料的结果。由于union没有记录当前存储的数据类型,所以在使用时需要确保正确理解其成员的含义,并避免出现未定义行为。一般来说,union应该在需要特殊的内存布局和节省内存时才使用,并且应该小心处理其中的数据。在现代C++编程中,更倾向于使用std::variant或std::any等类型安全的替代方案。