c++的一些陌生用法记录
1. 完美转发std::forward<decltype(PH1)>(PH1)
2. static的用法
-
static函数与普通函数的区别: 用static修饰的函数,本限定在本源码文件中,不能被本源码文件以外的代码文件调用。 而普通的函数,默认是extern的,也就是说,可以被其它代码文件调用该函数。 在函数的返回类型前加上关键字static,函数就被定义成为静态函数
-
不用实例化对象就可以调用类中的静态成员函数。如果没有实例化对象,直接调用类的非静态函数会报类似cannot call member function ’A()‘ without object;
-
需要注意,一个变量或者函数在同一个工程中,不能同时使用static和extern关键字修饰。因为在 C/C++ 中,extern 关键字用于声明一个全局变量或函数是在其他文件中定义的,而 static 关键字则用于声明一个变量或函数是在当前文件中定义的,只在当前文件的作用域内可见。因此,同一个变量或函数不能同时被声明为 extern 和 static。
3. placement new的用法
placement new 可以在地址没有确定的情况下构建对象。
placement new 是一种特殊的 new 操作符,它允许你在已经分配了内存的地址上构建对象,而不是让 new 操作符分配内存。通常情况下,你会手动分配内存,然后使用 placement new 在这个已分配的内存上构建对象。
由于 placement new 不涉及内存分配,而是使用你提供的地址进行构建,因此你可以在编译时并不知道确切地址的情况下使用它。这种灵活性使得它在某些情况下非常有用,比如在特定的内存池中构建对象,或者在堆栈上分配对象
char singBuf[sizeof(Sing)]; // 编译器将为singBuf分配一块内存,但具体地址尚未确定
// 在singBuf的内存上使用placement new构建Sing对象
Sing* singPtr = new (singBuf) Sing();
这种情况下,singBuf是一个字符数组,其内存地址由编译器分配,但是具体地址在编译时尚未确定。你可以使用 placement new 在这个数组的内存上构建对象,因为编译器会为 new (singBuf) Sing() 提供所需的地址信息
4. const容易遗忘的点
- 我们定义的类的成员函数中,常常有一些成员函数不改变类的数据成员,也就是说,这些函数是"只读"函数,而有一些函数要修改类数据成员的值。如果把不改变数据成员的函数都加上const关键字进行标识,显然,可提高程序的可读性。其实,它还能提高程序的可靠性,已定义成const的成员函数,一旦企图修改数据成员的值,则编译器按错误处理。 const成员函数和const对象 实际上,const成员函数还有另外一项作用,即常量对象相关。对于内置的数据类型,我们可以定义它们的常量,用户自定义的类也一样,可以定义它们的常量对象。
- 非静态成员函数后面加const(加到非成员函数或静态成员后面会产生编译错误)
- 表示成员函数隐含传入的this指针为const指针,决定了在该成员函数中,
任意修改它所在的类的成员的操作都是不允许的(因为隐含了对this指针的const引用); - 唯一的例外是对于mutable修饰的成员。
加了const的成员函数可以被非const对象和const对象调用
但不加const的成员函数只能被非const对象调用
5. Vector push_back 的时候使用std::move的益处
6. 数组的遍历方式性能
力扣79题的耗时对比:
使用索引遍历时间超过1000ms。使用范围base遍历,时间100多ms
6.1 索引遍历和范围base遍历的性能
本地vs debug模式性能对比:
索引遍历: 2.2s
范围base遍历: 6.5s
#include<vector>
#include<iostream>
#include<chrono>
using namespace std;
class Solution {
public:
vector<vector<int>> dxy{ {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
bool process(vector<vector<char>>& board, string& word, int c, int r, int t, int m, int n, vector<vector<bool>>& vis)
{
if (t == word.size() - 1)
{
return true;
}
for(int i = 0; i < 4; i++)
//for (auto xy : dxy)
{
int nc = c + dxy[i][0];
int nr = r + dxy[i][1];
if (nc < 0 || nc >= m || nr < 0 || nr >= n || vis[nc][nr] || board[nc][nr] != word[t + 1]) continue;
vis[nc][nr] = true;
if (process(board, word, nc, nr, t + 1, m, n, vis)) return true;
vis[nc][nr] = false;
}
return false;
}
bool exist(vector<vector<char>>& board, string word) {
int m = board.size();
int n = board[0].size();
vector<vector<bool>> vis(m, vector<bool>(n, false));
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (board[i][j] != word[0]) continue;
vis[i][j] = true;
if (process(board, word, i, j, 0, m, n, vis)) return true;
vis[i][j] = false;
}
}
return false;
}
};
int main()
{
vector<vector<char>> dp = {{ 'A','A','A','A','A','A'},
{'A','A','A','A','A','A'},
{'A','A','A','A','A','A'},
{'A','A','A','A','A','A'},
{'A','A','A','A','A', 'B'},
{'A','A','A','A', 'B','A'}};
string ss = "AAAAAAAAAAAAABB";
auto start = std::chrono::high_resolution_clock::now();
Solution S;
S.exist(dp, ss);
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = end - start;
std::cout << "程序运行时间:" << elapsed.count() << " 秒" << std::endl;
return 0;
}
6.2 静态数组和动态数组的索引遍历性能和初始化方式
6.3 类的成员变量初始化方式注意
- 不合法
因为这种形式的初始化只能在构造函数或成员函数中使用,而不能在类的成员变量处进行。
class Solution
{
vector<vector<int>> m_vis(6, vector<int>(6, 0));
};
- 合法(在)
可以在构造函数中初始化vis数组,或者将其声明为成员变量后在构造函数中进行初始化
class Solution
{
vector<vector<int>> m_vis;
Solution(): (6, vector<int>(6, 0)){}
};