1.27、唠嗑
P87、C++静态分析
静态分析工具:PVS studio
好像可以在VS中作为一个插件
没什么东西。
P88、C++的参数计算顺序
C++17新特性:后缀表达式必须在别的表达式之前被计算,约定两件事不能同时做。
函数传参,参数连续两个自增,这种行为属于C++未定义行为,具体结果和运算顺序因编译器而定。
P89、C++移动语义 (C++11)
前置知识:左值和右值
移动语义本质上允许我们移动对象(C++11以后引入右值引用),尽可能避免拷贝和分配内存。
移动语义就像是拿一个盒子去扣住一个球,而不是复制一个球再放进盒子里。
#include<iostream>
class String
{
public:
String() = default;
String(const char* string)
{
printf("Created!\n");
m_Size = strlen(string);
m_Data = new char[m_Size];
memcpy(m_Data, string, m_Size);
}
String(const String& other)
{
printf("Copied!\n");
m_Size = other.m_Size;
m_Data = new char[m_Size];
memcpy(m_Data, other.m_Data, m_Size);
}
//偷来了资源,move含义所在
String(String&& other)noexcept
{
printf("Moved!\n");
m_Size = other.m_Size;
m_Data = other.m_Data;
other.m_Size = 0;
other.m_Data = nullptr;
}
~String()
{
printf("Destroyed\n");
delete m_Data;
}
void Print()
{
for (uint32_t i = 0; i < m_Size; i++)
printf("%c", m_Data[i]);
printf("\n");
}
private:
char* m_Data;
uint32_t m_Size;
};
class Entity
{
public:
Entity(const String& name)
: m_Name(name)
{
}
Entity(String&& name)
: m_Name((String&&)name)
//: m_Name(std::move(name))
{
}
void PrintName()
{
m_Name.Print();
}
private:
String m_Name;
};
int main()
{
Entity entity("Cherno");
entity.PrintName();
std::cin.get();
}
P90、std::move与移动赋值操作符
C++三法则:如果需要析构函数,则一定需要拷贝构造函数和拷贝赋值操作符。
C++五法则:为了支持移动语义,又增加了移动构造函数和移动赋值运算符。
//P89代码的基础上
String& operator=(String&& other) noexcept
{
if (this != &other)
{
//防止自身已经存储数据造成内存泄漏
delete m_Data;
printf("Moved!\n");
m_Size = other.m_Size;
m_Data = other.m_Data;
other.m_Size = 0;
other.m_Data = nullptr;
}
return *this;
}
int main()
{
//Entity entity("Cherno");
//entity.PrintName();
String apple = "apple";
String dest;
//String dest = std::move(apple); 这里是移动构造函数
apple.Print();//apple
dest.Print();//空
dest = std::move(apple); //这里是移动赋值运算符。
apple.Print();//空
dest.Print();//apple
std::cin.get();
}
P91、自己实现Array数组
fixed size and stack allocated array
#include<iostream>
template<typename T, size_t S>
class Array
{
public:
constexpr size_t Size() const { return S; }
T& operator[](size_t index) { return m_Data[index]; }
const T& operator[](size_t index) const { return m_Data[index]; }
T* Data() { return m_Data; }
const T* Data() const { return m_Data; }
private:
T m_Data[S];
};
int main()
{
Array<int, 5> arr;
memset(&arr[0], 0, arr.Size() * sizeof(int));
arr[3] = 4;
const auto& arrRef = arr;
for (size_t i = 0; i < arrRef.Size(); i++)
{
std::cout << arrRef[i] << std::endl;
}
Array<std::string, 2> data;
data[0] = "Cherno";
data[1] = "C++";
for (size_t i = 0; i < data.Size(); i++)
{
std::cout << data[i] << std::endl;
}
std::cin.get();
}
P92、自己实现Vector数组
搬运别人的笔记:https://www.cnblogs.com/zhangyi1357/p/16009968.html
1.28、开学吧
P93、C++中的迭代器
用法要熟练:
#include<iostream>
#include<vector>
#include<unordered_map>
int main()
{
std::vector<int> values = { 1,2,3,4,5 };
for (std::vector<int>::iterator it = values.begin();
it != values.end(); it++)
{
std::cout << *it << std::endl;
}
std::unordered_map<std::string, int> map;
map["Cherno"] = 5;
map["C++"] = 2;
for (std::unordered_map<std::string, int>::const_iterator it = map.begin();
it != map.end(); it++)
{
std::cout << it->first << " : " << it->second << std::endl;
}
//std::pair<const std::string, int> kv
for (auto kv : map)
{
auto& key = kv.first;
auto& value = kv.second;
std::cout << key << " : " << value << std::endl;
}
//C++17 结构化绑定 P75
for (auto& [key, value] : map)
{
std::cout << key << " : " << value << std::endl;
}
std::cin.get();
}
P94、写一个迭代器
(本P没有字幕,依据P92的代码在改)
不如看侯捷STL (摸了
P95、How to REALLY learn C++
省流:做项目&软件广告PS studio
P96、C++中的二进制和按位运算符
讲了一堆二进制(科普级)
P97~P99都没啥用
P100、Maps in C++ (std::map and std::unordered_map)
unordered_map需要重写hash
map 需要重写 < 操作
std::unordered_map中 map[key] 这个操作具有改变(insert)map的能力,故与最好用 map.at(key)
P101、NULL
NULL是一个宏:C++中为0,C中为 ((void *)0)
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
nullptr是C++语法,值为0,字节长度与平台有关。
一个关于nullptr类中this的原理例子,什么时候会崩溃。可以取地址,但不能读值。
offsetof宏:可以求类或结构体的中成员的内存偏移
#if defined _MSC_VER && !defined _CRT_USE_BUILTIN_OFFSETOF
#ifdef __cplusplus
#define offsetof(s,m) ((::size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))
#else
//注意这行,s为结构体名,以0为基址求m的内存地址。
#define offsetof(s,m) ((size_t)&(((s*)0)->m))
#endif
#else
#define offsetof(s,m) __builtin_offsetof(s,m)
#endif