特殊工具与技术C++

Posted atohome

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了特殊工具与技术C++相关的知识,希望对你有一定的参考价值。

第19章 特殊工具与技术

到此你会感觉C++越来越离谱,不好好想着解决问题,语法与特性先成为了一大问题。只能说太复杂了,上手难度较高。

本章分别从,控制内存分配、运行时类型识别、枚举类型、类成员指针、嵌套类、union联合体、局部类、不可移植的特性,入手进行学习

重载new和delete

太离谱了吧,new与delete还能重载!先回顾一下new与delete,下面使用到了多维数组的内存动态分配,在C++中new与delete就相当于C中的malloc与free函数

//example1.cpp
#include <iostream>
#include <string>
using namespace std;

int main(int argc, char **argv)

    string *str = new string();
    delete str;
    int(*m)[5] = new int[5][5];
    m[0][0] = 1;
    m[4][4] = 1;
    cout << m[0][0] << " " << m[4][4] << endl; // 1 1
    delete[] m;
    return 0;

当用户自定义了new与delete 的operator,则有限使用自定义的,没找到则将寻找new与delete的函数重载,与之前的<,>操作函数类似,否则将会使用标准库中的new、delete

标准库中有4个delete重载、4个new重载

void *operator new(size_t);//分配一个对象
void *operator new[](size_t);//分配一个数组
void *operator delete(void*) noexcept;//释放一个对象
void *operator delete[](void*) noexcept;//释放一个数组

void *operator new(size_t,nothrow_t&) noexcept;//分配一个对象
void *operator new[](size_t,nothrow_t&) noexcept;//分配一个数组
void *operator delete(void*,nothrow_t&) noexcept;//释放一个对象
void *operator delete[](void*,nothrow_t&) noexcept;//释放一个数组

总之我们左右不了new与delete的行为,我们做的就是写好构造函数与析构函数防止内存泄露

malloc与free函数

#include<cstdlib>

例如以下是使用malloc和free编写new与delete的方法

//example2.cpp
#include <iostream>
#include <cstdlib>
#include <stdexcept>
using namespace std;

void *operator new(size_t size)

    cout << "new memory" << endl;
    if (void *mem = malloc(size))
    
        return mem;
    
    else
    
        throw bad_alloc();
    


void operator delete(void *mem) noexcept

    cout << "delete memory" << endl;
    free(mem);


int main(int argc, char **argv)

    
        int *num = new int();
        *num = 100;
        cout << *num << endl; // new memory 100
        delete num;
    
    return 0;

定位new表达式

与allocator类的allocate(size)与deallocate(p,size)的功能有异曲同工之妙。定位new允许在一个特定的、预先分配的内存地址上构造对象

new (place_address) type
new (place_address) type (initializers)
new (place_address) type [size]
new (place_address) type [size] braced initializer list
//example3.cpp
#include <iostream>
using namespace std;

int main(int argc, char **argv)

    char *buffer = new char[12];
    int *p1 = new ((void *)buffer) int;
    *p1 = 1;
    cout << (int)buffer[0] << " " << (int)buffer[1] << " " << (int)buffer[2] << " " << (int)buffer[3] << endl;
    //        10000000                  00000000                00000000                00000000
    char *p2 = new ((void *)buffer) char[12]1, 2, 3, 4;
    cout << (int)p2[0] << (int)p2[1] << (int)p2[2] << (int)p2[3] << endl;
    //        1               2              3           4
    return 0;

显式调用析构函数

构造函数的调用都是在使用栈内存定义变量时或者使用动态内存分配时进行调用,但是以前我们默认认为在内存释放时,析构函数自动调用,但是C++允许显式调用析构函数的操作

显式调用析构函数与allocator的destory§方法类似,调用后析构函数被执行,但是内存并没有被释放掉,内存可以重新进行使用

//example4.cpp
#include <iostream>
#include <string>
using namespace std;

int main(int argc, char **argv)

    string *p1 = new string();
    p1->~string(); //调用构造函数并不释放内存
    *p1 = "dss";
    delete p1;
    // cout << *p1 << endl;//错误 乱码
    int(*m)[5] = new int[4][5];
    return 0;

运行时类型识别

在开发中在程序运行时,有时有需求判断一个变量是那种数据类型
运行时类型识别(run-time type identification,RTTI),主要有两种方式

1、typeid运算符,返回表达式的类型
2、dynamic_cast运算符,将基类指针或引用安全地转为派生类指针或引用

dynamic_cast运算符

dynamic_cast使用形式

dynamic_cast<type*>(e);
dynamic_cast<type&>(e);
dynamic_cast<type&&>(e);
//e为nullptr时则返回nullptr

指针类型dynamic_cast

指针型dynamic_cast转换失败时会返回空指针

//example5.cpp
#include <iostream>
#include <stdexcept>
using namespace std;

class A

public:
    virtual void test() = 0;
;

class B : public A

public:
    void test() override 
;

class C : public A

public:
    void test() override 
    void hello()
    
        cout << "hello world" << endl;
    
;

int main(int argc, char **argv)

    B *b = new B();
    A *a = b;
    B *b1 = dynamic_cast<B *>(a); // A至少要有一个虚函数

    C *c = dynamic_cast<C *>(a); //去a的基类部分构造c
    c->hello();
    delete c;
    delete b;
    return 0;

引用类型dynamic_cast

引用类型转换失败则会抛出std::bad_cast异常

//example6.cpp
class A

public:
    virtual void test() = 0;
;

class B : public A

public:
    void test() override
    
        cout << "test" << endl;
    
;

class C : public A

public:
    void test() override
    
        cout << "C" << endl;
    
;

int main(int argc, char **argv)

    B b;
    A &a = b;
    B &b1 = dynamic_cast<B &>(a);
    b1.test(); // test

    try
    
        C c;
        A &a = c;
        dynamic_cast<B &>(a);
    
    catch (bad_cast e)
    
        cout << e.what() << endl; // std::bad_cast
    
    return 0;

RTTI实战

编写自定义类的equal方法

//example7.cpp
#include <iostream>
using namespace std;

class A

public:
    bool operator==(A &other)
    
        return typeid(*this) == typeid(other) && this->equal(other);
    

protected:
    virtual bool equal(A &other)
    
        return true;
    
;

class B : public A

public:
    int num;
    B(int num) : num(num)
    
    

protected:
    bool euqal(A &other)
    
        auto r = dynamic_cast<B &>(other);
        return num == r.num;
    
;

class C : public A

protected:
    bool euqal(A &other)
    
        auto r = dynamic_cast<B &>(other);
        return true;
    
;

int main(int argc, char **argv)

    B b(12);
    A &a1 = b;
    A &a2 = b;
    cout << (a1 == a2) << endl; // 1
    C c;
    A &a3 = c;
    cout << (a1 == a3) << endl; // 0 派生类类型不同
    return 0;

typeid运算符

typeid运算符返回type_info对象

//example8.cpp
#include <iostream>
using namespace std;

int main(int argc, char **argv)

    int a;
    const type_info &info1 = typeid(a);
    cout << info1.name() << endl; // i

    double d_num;
    const type_info &info2 = typeid(d_num);
    cout << info2.name() << endl; // d

    cout << (info1 == info2) << endl; // 0
    return 0;

type_info类

type_info的定义可能根据编译器的不同而不同

#include<typeinfo>

其没有默认构造函数,它的拷贝和移动构造函数、赋值运算符都被定义成了删除的,创建type_info的唯一途径就是使用typeid操作

//example9.cpp
class A

public:
    virtual void test()

    ;
;

class B : public A

public:
    void test() override
    
    
;

int main(int argc, char **argv)

    B b;
    cout << typeid(b).name() << endl; // 1B
    A a;
    cout << typeid(a).name() << endl; // 1A
    A &a_ref_b = b;
    cout << typeid(a_ref_b).name() << endl; // 1B
    A *a_ptr_b = &b;
    cout << typeid(a_ptr_b).name() << endl;  // P1A
    cout << typeid(*a_ptr_b).name() << endl; // 1B
    return 0;

枚举类型

C++中有两种枚举:限定作用域和不限定作用域的

1、限定作用域的

//example10.cpp
#include <iostream>
using namespace std;

enum class m_enum

    a,
    b,
    c,
    d
;

int main(int argc, char **argv)

    bool res = m_enum::a == m_enum::b;
    cout << res << endl;                      // 0
    cout << (m_enum::a == m_enum::a) << endl; // 1
    return 0;

2、不限定作用域的

//example11.cpp
#include <iostream>
using namespace std;

enum color //不限作用域

    red,
    blue
;

enum //未命名且不限作用域

    yellow,
    pink
;

int main(int argc, char **argv)

    cout << (red == blue) << endl; // 0
    // cout << (red == yellow) << endl;//warning: comparison between 'enum color' and 'enum<unnamed>'
    // 1
    return 0;

枚举成员

默认情况下枚举值从0开始,依次加1

//example12.cpp
#include <iostream>
using namespace std;

enum

    red,
    pink
;

enum color

    /// red, //冲突
    // pink
    black
;

enum class person

    man,
    woman
;

int main(int argc, char **argv)

    color c1 = black;
    person p1 = person::man;
    // color c2 = red;//错误
    return 0;

自定义枚举成员的值

默认从0依次加1,但允许用户自定义值

//example13.cpp
#include <iostream>
using namespace std;

enum class color

    pink,
    red = 12,
    black,
    blue = 3
;

int main(int argc, char **argv)

    color c1 = color::black;
    color c2 = color::red;
    cout << (int)c1 << endl;          // 13
    cout << (int)c2 << endl;          // 12
    cout << (int)color::pink << endl; // 0
    return 0;

枚举成员与常量表达式

枚举成员为const,所以在初始化枚举成员时提供的初始值必须为常量表达式,每个枚举成员本身就是一条常量表达式

//example14.cpp
#include <iostream>
using namespace std;

enum class color

    red,
    pink
;

int main(int argc, char **argv)

    const int n = 100;
    constexpr int num = n;
    cout << num C++ Primer 5th笔记(chap 19 特殊工具与技术)类成员指针

特殊工具与技术C++

特殊工具与技术C++

特殊工具与技术C++

C++:特殊工具与技术之union(联合-共用体)

C++:特殊工具与技术之union(联合-共用体)