C++学习笔记(2)

1.关键字 this

基本的用法如下,此外在调用外部的函数时,也可以通过this关键字来传递指向对象的指针

class Entity
{
public:
    int x,y;

    Entity(int x, int y)
    {
        # Entity* e = this;        
        this->x = x;
        this->y = y;  #this关键字代表了指向对象自己的指针
    }

    int GetX() const
    {
        # const Entity* e=this;

        return x;
    }
};

2. 智能指针(unique pointer, shared pointer, weak pointer)

创建对象后无需再调用delete去删除

unique pointer在程序运行结束后会自动调用delete函数,特点是不能复制,因为如果复制之后,就会有两个指针指向这个地址,一个自动删除了另外一个就指向了错误的或者说不明所以的地址。

# include <iostream>
# include <string>
# include <memory>  #调用智能指针需要用的库

class Entity
{
public:
    Entity()
    {
        std::cout << "Created Entity" << std::endl;
    }

    Entity()
    {
        std::cout << "Destroyed Entity" << std::endl;
    }
};

int main()
{
    {
        std::unique_ptr<Entity> entity(new Entity());
        std::unique_ptr<Entity> e0 = entity;  #这么做程序会报错,因为unique pointer不能复制
    }
} 

shared pointer主要用来做引用计数,用来跟踪指针的引用数量,当数量为0时,此指针会被自动删除。weak pointer是配合shared指针来用的,当将shared指针分配给弱指针时,它不会增加计数,一般用来做确认,确认该指针是否还在。

int main()
{
    {
        std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
        std::shared_ptr<Entity> e0 = sharedEntity;
    }
}

3.Copy Constructor

这个用来为函数或类进行deep copy,其目的是复制时不会仅仅复制指向该类的指针,而是为整个复制的类提供新的指针和存储空间。

class String
{
private:
    char* m_Buffer;
    unsigned int m_Size;
public:
    String(const char* string)
    {

    }

    String(const String& other)
        : m_Buffer(other.m_buffer), m_Size(other.m_Size)
    {

    }

    ~String()
    {
        delete[] m_Buffer;  #如果只是像是主函数里简单的复制,实际上是复制一份指向该类的指针,程序运行结束后会报错,因为调用了两遍删除。
    }

};

int main()
{
    String string = "Pete";
    String Second = string;
}

4.箭头运算符->

相当于将解引用和成员访问符两个操作符结合到一起,是一种对象指针更方便访问对象成员的方法。

#include <iostream>
#include <string>

class Entity
{
public:
    int x;
public:
    void Print() const { std::cout << "Hello" << std::endl; }
}

int main()
{
    Entity e;
    e.Print();

    Entity* ptr = &e;
    ptr.Print() #这是不可以的,不能用指针去掉用传递变量的函数

    ptr->Print(); #这是可以的,相当于下面两行代码

    Entity& entity = *ptr;
    (*ptr).Print();

    std:cin.get();
}

->还可以用来确定偏移量:

#include <iostream>
#include <string>

struct Vector3
{
    float x, y, z;
};

int main()
{
    int offset = (int)&((Vector3*)nullptr)->x;  #打印0
    int offset = (int)&((Vector3*)nullptr)->y;  #打印4
    std::cout << offset << std::endl;

    std::cin.get();
}

5.动态数组

#include <iostream>
#include <string>
#include <vector>  #动态数组引入这个库

struct Vertex
{
    float x, y, z;
};

std::ostream& operator<<(std::ostream& stream, const Vertex& vertex)
{
    stream << vertex.x << ", " << vertex.y << ", " << vertex.z;
    return stream;
}

int main()
{
    std::vector<Vertex> vertices;
    vertices.push_back({ 1, 2, 3 });
    vertices.push_back({ 4, 5, 6 });

    for (int i = 0; i < vertices.size(); i++)
        std::cout << vertices[i] << std::endl;

    for (Vertex& v : vertices)
        std::cout << v << std::endl;

    vertices.clear();

    std::cin.get(); 
}


优化动态数组,因为每次调用push_back都会造成内存两次重新分配,一次为从栈中放入vector中,另外一次为为前一次调用重新分配对象个数

vertices.reserve(3);  #这个代码告诉了系统要分配三个对象的空间
vertices.emplace_back(1, 2, 3);  
vertices.emplace_back(4, 5, 6);    #这两行告诉系统在创建时就将他们创建到为vector分配的空间中,而不是主函数的栈中

6.使用库

在库文件中,有头文件(include)和库文件(lib)两种,库的使用也分为静态库和动态库两种。

头文件向我们提供声明,告诉我们哪些函数可用;库文件为我们提供定义,这样我们就可以链接到这些函数并执行相关的代码。

动态库链接发生在运行过程,在运行时会链接。静态库链接发生在编译过程。

$(SolutionDir)可以构建相对路径,在静态库连接时先在C++和Linker下的general内将库的路径加入进来 ,然后再input中将库文件名字加到依赖中。

在动态库链接时要将依赖改为用于动态链接的库文件,并且将dll文件放入到执行文件目录中

7.元组(返回不同类型的变量)

#include <iostream>
#include <string>
#include <tuple>

static std::tuple<std::string, std::string> ParseShader(const std::string& filepath)
{
    #....
    std::string vs = ss[0].str();
    std::string fs = ss[1].str();

    return std::make_pair(vs, fs);
}

static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{
    #....
}

int main()
{
    #....
    std::tuple<std::string, std::string> sources = ParseShader("filepath");
    auto sources = ParseShader("filepath");  #这两个都行
    unsigned int shader = CreatShader(std::get<0>(sources), std::get<1>(sources));
}

或者下面这种: 

struct ShaderProgramSource
{
    std::string VertexSource;
    std::string FragmentSource;
}

static ShaderProgramSource ...
{
    #....
    return { vs, fs };
}

int main ()
{
    #....
    unsigned int shader = CreatShader(sources.VertexSource, sources.FragmemtSource);
}

8.模板(template)范型编程(函数)

https://www.cnblogs.com/wangduo/p/5559260.html

模板在被实际调用之前并不存在

#include <iostream>
#include <string>

template<typename T>
void Print(T value)
{
    std::cout << value << std::endl;
}

int main()
{
    Print(5);
    Print("Hello");
}

9.宏(macros)

一种纯粹的文本替换,尽量不要过多的使用,因为可能会造成代码不好阅读

#include <iostream>
#include <string>

#define WAIT std::cin.get()

int main()
{
    WAIT;
}

 可以通过在debug模式下的预处理器中插入PR_DEBUG定义,这样做可以将一些代码限制在debug模式下,在release模式下不会运行。

#include <iostream>
#include <string>

#ifdef PR_DENUG
#define LOG(x) std::cout << x << std::endl;
#else
#define LOG(x)
#endif

int main()
{
    LOG("hello");
    std::cin.get();
}

10.auto关键字

auto可以自动确定变量的类型,当需要使用的类型非常长的时候,为了代码简洁,可以通过auto替换,增加代码的可读性

#include <iostream>
#include <string>

std::string Getname()
{
    return "Pete";
}

int main()
{
    auto name = Getname();  #这里加auto关键字可以自动获取调用函数返回值的类型,并且函数变换类型也不会影响

    std::cin.get();
}

11.函数指针

函数指针是指向函数地址的指针

#include <iostream>

void HelloWorld()
{
    std::cout << "Hello World" << std::endl;
}

int main()
{
    auto function = HelloWorld;  #function被赋予helloworld函数的地址
    function();  #调用function即调用了helloworld
}

12.Lambdas

是一种在被调用的位置或作为参数传递给函数的位置定义匿名函数对象(闭包)的简便方法。 Lambda 通常用于封装传递给算法或异步函数的少量代码行。

#include <iostream>
#include <vector>
#include <algorithm>

void ForEach(const std::vector<int>& values, void(*func)(int)) #function pointer
{
    for (int value : values)
        func(value);
}

void ForEach(const std::vector<int>& values, const std::function<void(int)>& func) #function
{
    for (int value : values)
        func(value);
}

int main()
{
    std::vector<int> values = { 1, 5, 4, 2, 3 };
    int a = 5;
    ForEach(values, [](int value){ std::cout << "Value: " << value << std::endl; }) #lambdas被调用用来确定函数指针指向的函数(function pointer)
    
    auto lambda = [=](int value){ std::cout << "Value: " << a << std::endl; }; #[]里的=意思是将值传入。
    ForEach(values, lambda);
    std::cin.get();
}

13.线程(thread)

#include <iostream>
#include <thread>

static bool s_finished = false;
void DoWork()
{
    while(!s_finished)
    {
        std::cout << "Working..\n";
    }
}

int main()
{
    std::thread worker(DoWork);

    std::cin.get();
    s_finished = true;

    worker.join();
    std::cout << "finished" << std::endl;

    std::cin.get();
}

14.计时

#include <iostream>
#include <chrono>
#include <thread>

int main()
{
    using namespace std::literals::chrono_literals;
   
    auto start = std::chrono::high_resolution_cloak::now();
    std::this_thread::sleep_for(1s);
    auto end = std::chrono::high_resolution_cloak::now();

    std::chrono::duration<float> duration = start - end;
    std::cout << duration.count() << "s" << std::endl;

    std::cin.get();
}

15.sorted

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

int main()
{
    std::vector<int> values = { 3, 5, 1, 4, 2 };
    std::sort(values.begin(), values.end(), [](int a, int b)
    {
        return a < b;  #会返回[1, 2, 3, 4, 5],因为sort函数第三个参数就是bool类型判断,如果a在b排列的前面,则返回真。a<b则a在b排列的前面,返回真
    });

    for(int value : values)
        std::cout << value << std::endl;

    std::cin.get();
}

16.Union

union 关键字的用法与struct 的用法非常类似。

union 维护足够的空间来置放多个数据成员中的“一种”,而不是为每一个数据成员配置空间,在union 中所有的数据成员共用一个空间,同一时间只能储存其中一个数据成员,所有的数据成员具有相同的起始地址。例子如下:

union StateMachine
{
   char character;
   int number;
   char *str;
   double exp;
};

一个union 只配置一个足够大的空间以来容纳最大长度的数据成员,以上例而言,最大长度是double 型态,所以StateMachine 的空间大小就是double 数据类型的大小。

参考:

C++关键字union_c++的关键字union_瞻邈的博客-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值