C++碎片化知识点记录(4)

1.C++的class中未加修饰符的成员变量是private属性的,struct中未加修饰符的成员变量是public属性的。

2.以下代码运行结果

int main() {
    char* s = "aaaaaa";
    cout << sizeof(*s) << endl << sizeof(s) << endl<<strlen(s)<<endl;
    return 0;
}

int main() {
    char s[] = "aaaaaa";
    cout << sizeof(*s) << endl << sizeof(s) << endl<<strlen(s)<<endl;
    return 0;
}

3.分析以下代码

void test(int a=1,int b=2,char* s="22"){
    if (!s) {
        cout << "null" << endl;
        return;
    }
    cout << a << " " << b << " " << s << "  "<<sizeof(s)<<endl;
}

int main() {
    test(3);//匹配a
    //test(3, "11");//形参匹配出错,形参需要按顺序一一匹配,不支持跳着匹配
    test(3, 'a');//匹配a b
    test(3, 2, 0);//0代表为空指针
    test(3, 2, "91222");//可正常匹配
    return 0;
}

4.const对字符串的修饰

int main() {
    char const* p = "abc";
    p = nullptr;


    const char* p1 = "abc";//p与p1等价,const均是修饰的指针所指的空间
     p1 = nullptr;

    char* const p2 = "123";//p2修饰的是指针,指针是常量。但因为"123"是字符串常量,本质上p2与p3是等价的
    //p2[0] = '1';
   // p2 = nullptr;

    const char* const p3 = "abc";//p2修饰的是指针和其所指的空间
   // p3 = nullptr;

    return 0;
}

5.判断进程运行在32位还是64位

1)用sizeof判断指针长度

int main() {
    int* p;
    cout << sizeof(p) << endl;//32位4字节,64位8字节
    return 0;
}

2)用预编译判断

bool getWindowsBit()
{
#if _WIN64
    return true;
#endif _WIN32
    return false;
}

3)其他的判断方法

参考:c++ 判断是64位还是32位系统的实例

C++怎么判断是64位系统还是32位系统 不用sizeof

6.静态成员变量不占据类的内存空间

class A {
    static int a ;
    int b;
};
int main() {
    cout << sizeof(A) << endl;//输出4,即A::b所占内存空间大小
    return 0;
}

7.自己写的类对象作为map和unordered_map键值要注意什么情况

参考:STL: unordered_map 自定义键值类型的使用(C++)_十觞亦不醉的博客-CSDN博客

C++ map和unordered_map自定义key_weixin_33858336的博客-CSDN博客

1)作为map键值

重载比较运算符定义对象时传入比较规则

2)作为unordered_map键值

写哈希函数并在定义哈希表时传入。

写键值相等函数并传入。

8.vector的容量(capacity)问题

未指定capacity的容量大小的话,那么初始的capacity大小与size是相等的,后面如果size大于capacity则会进行1.5倍的扩容,并且vector的扩容均摊时间复杂度未O(3)

参考:vector中push_back()的时间复杂度(字节一面)_智慧的人不要秃头的博客-CSDN博客_vector时间复杂度

9.引用折叠与完美转发

在给函数形参赋值的时候,我们需要让形参保持为左值引用还是右值引用的形式的话就需要用到完美转发。

1)首先要理解引用折叠的规则,参考:
c++引用折叠,万能引用,完美转发_急支糖浆123的博客-CSDN博客_c++ 引用折叠

引用折叠是C++的一种编译规则,编译器会自动对不同个数的引用符号进行折叠将其转换为左值引用或右值引用。

2)万能引用

万能引用是模板跟引用折叠的结合。形参类型声明为T&&时,会对传入的实参进行自动匹配

3)完美转发

在理解万能引用的基础上引入一个std::forward,std::forward本质是一个强制转换T&&,本质上还是用到了引用折叠。

参考:左值引用、右值引用、移动语义、完美转发,你知道的不知道的都在这里 - 知乎

谈谈完美转发(Perfect Forwarding):完美转发 = 引用折叠 + 万能引用 + std::forward - 知乎

10.四种强转

参考:C++四种强制类型转换_欧特克_Glodon的博客-CSDN博客_c++强制类型转换

c++ 四种强制类型转换介绍_ydar95的博客-CSDN博客_c++ 转换类型

C++的四种强制转换_很难绷得住的博客-CSDN博客_c++强制转换

1)static_cast

静态的强转,在编译时就实现强转,使用起来有一定的风险,跟C风格的强转很相似。

举例说明:

/* class 的上下行转换 */
class Base {
public:
    int a = 0;// something
};
class Sub :public Base {
public:
    // something
    int a = 1;
};

int main()
{
    //  上行 Sub -> Base
//编译通过,安全
    Sub sub;
    Base* base_ptr = static_cast<Base*>(&sub);
    cout << base_ptr->a << endl;
    //  下行 Base -> Sub
    //编译通过,不安全
    Base base;
    Sub* sub_ptr = static_cast<Sub*>(&base);
    cout << sub_ptr->a << endl;
}

 2)dynamic_cast

动态的强转,即会在运行时进行强转,是RTTI机制的一个典例。其主要用于基类和派生类之间的强转,因为其借助的RTTI机制,因此需要在基类中定义一个虚函数。

/* class 的上下行转换 */
class Base {
public:
    int a = 0;// something
    virtual ~Base(){}
};
class Sub :public Base {
public:
    // something
    int a = 1;
};

int main()
{
    //  上行 Sub -> Base
//编译通过,安全
    Sub sub;
    Base* base_ptr = dynamic_cast<Base*>(&sub);
    cout << base_ptr->a << endl;
    //  下行 Base -> Sub
    //编译通过,但运行时会将sub_ptr设置为空指针
    Base base;
    Sub* sub_ptr = dynamic_cast<Sub*>(&base);
    cout << sub_ptr->a << endl;//空指针,无法使用
}

3)const_cast

这是这几个强转里面唯一一个可以用于const转换的,其强转的目标需要是引用或指针。

但是有一个注意点,源目标如果是const类型的非指针或引用类型的变量,那么源目标是无法听过解除强转的指针或引用去修改其值的。

举例:

int main()
{
    const int a = 1;
    const int* b = &a;//const_cast<int*>(&a)
    int* c = const_cast<int*>(b);
    *c = 5;
    std::cout << a<<endl<<(*b)<<endl<<(*c)<<endl;//输出1 5 5
}

上面的代码中通过修改(*c)只能修改(*b)的值,无法修改变量a的值。

int main()
{

    const int* ptr = new int(7);
    int* e = const_cast<int*>(ptr);
    *e = 5;
    std::cout  << (*ptr) << endl << (*e);//输出5 5
}

但是如果是一个常量指针的话则是可以正常修改的。

除此之外,还用于常量成员函数中。

class base {
    int a = 0;
    void test()const{
        ++const_cast<base*>(this)->a;//直接++a是错的
        cout << a << endl;
    }
};

4)reinterpret_cast

这是最为危险的指针,但是非常灵活,static_cast在使用时还是会进行必要的检测(诸如指针越界计算,类型检查)。

而reinterpret_cast可以随便转。

class base{};
int main()
{
    base b;
    int* a = reinterpret_cast<int*>(&b);

猜你喜欢

转载自blog.csdn.net/qq_42987967/article/details/126914877