语言/CPP {CPP知识汇总}
关键字
friend
使用1: 全局有void F()
这个函数的定义, 此时你在类里面写上friend void F();
这个声明, 意味着 这个函数 可以访问当前类的私有;
使用2: 直接在类里面写friend void F(){ ...}
这个定义, 就相当于 你在全局定义了一个void F(){...}
函数, 换句话说, 如果此时你在全局写void F(){..}
是会报错的!
.
因此 friend
除了使用1这种功能, 他还有个功能: 在类内部 定义全局函数, 他不是成员函数;
@DELI;
写在类里面, 有2种用法:
class ST{
private:
int data;
//>> 不存在`private/public`的情况, 因为他不是该类的成员;
friend void F1( ST);
friend void F2( ST _a){ cout<< _a.data;}
};
void F1( ST _a){ cout<< _a.data;}
F1, F2
是完全一样的 (都是全局函数, 不是类的成员函数 因此函数里没有this
这个东西), F1
这种用法比较常见, F2
用法比较新颖 (他合并到一起了);
constexpr
constexpr
对象;
如果一个类的构造函数 是constexpr
函数, 则对象是literal
字面类型;
该对象里的数据, 都必须是字面类型;
@DELI;
constexpr
函数
构造函数, 成员函数, 全局函数, 都可以用constexpr
来修饰;
constexpr int Func( int _x){ ...}
我猜他的本质是:
.
由于#define OP_( _x) _x * _x * _x
这种方式, 如果你_x
放的是个函数 那么他会执行3次, 当然你的本意是想让他执行1次;
为了实现此, 即执行一个op(x)
操作 (当然可以用函数实现, 但毕竟函数调用有开销), 又能实现对_x
函数 只调用1次, constexpr
函数 我想大概如此 (即 虽然他是函数的样子, 但实际调用时 会优化成类似于宏定义, 而不是函数开销);
constexpr
函数里, 只能使用: literal
字面类型和constexpr
函数;
constexpr int F( int _x){
ST t(0); // ST是字面类型, 即他的构造函数是`constexpr`;
ST2 * s = ?; // ST2不是字面类型, 但`T *`是指针 也是字面类型;
for( int i = 0; i < _x; ++i) _x += i;
return _x;
}
static
@DELI;
类的静态函数;
class ST{
static int Func(){ ...}
};
这是错误的; 类的静态函数, 他的实现 不可以放到类声明里, 必须要单独拿出来;
换句话说, 他必须写成声明和实现分开的形式; 而且最重要的是: 声明时 要写static
, 而实现时 不可以写static
;
class ST{
static int Func();
};
int ST::Func(){ ...};
以上是正确的; (如果你写成static int ST::Func(){ ...}
就错了);
template
类的特化, 是完全不同的类;
template< int A> class ST{
public:
int f;
void F(){}
};
template<> class ST<0>{
public:
int f0;
void F0(){}
};
template<> class ST<1>{
public:
int f1;
void F1(){}
};
你现在有3个完全不同的类 (他们的数据/函数 都不同), 即ST< !={0,1}>
ST<0>
ST<1>
;
你只要看到template<...> class ST{};
(即ST
类名是单独的) 他就是根, 即一切的特化 都是源自他的;
否则, 只要是template<...> class ST<?>{};
(即ST<?>
类名后面有模板) 他就是特化版本;
@DELI;
不同的模板,是复制了多份不同的代码;
template< int _T> //< it is equivalent to `<class _T>` in here
void F(){
static int a = 0;
++ a;
cout<< a;
}
F<1>();
F<2>();
, finally, the output is
1
;
2
1; 2
1;2;
But F<1>();
F<2>();
where there are two different
a
a
a, so the output is
1
;
1
;
1;1;
1;1;
F< 1>();
, you can consider it as there is a function void F_1()...
; so, F<2>()
means a function void F_2()...
; they are totally different.
@DELI;
函数模板;
函数模板(不管是全局函数 还是类的函数) 与 类模板, 有一点不同;
模板类在实现时 必须写成template< class _T> ST<_T>::xxx
, 即类名后面要带上<_T>
(不仅用户使用时要这样, 连实现时也要带上);
可是, 模板函数 不管是声明还是实现时 必须写成如下形式:
template< class _T> void Func( _T);
template< class _T> void Func( _T){ ...}
不可以写成... Func< _T>( _T)...
(只有是用户使用时 才可以写成Func<int>(...)
);
@DELI;
template< class _Type>
模板参数 (分为2种: {类型, 整数常量}), 对于类型参数 是可以放你自定义的类型的;
template< class _Type>
void Func(){
_Type obj; // MARK: @LOC_0;
cout<< obj.data;
}
class ST{
public: string data;
};
以上代码是正确的, 你调用Func< ST>()
这是没问题的, 虽然LINK: @LOC_0
在编辑期间 无法知道_Type
的类型 (IDE的提示 比如自动补全 肯定不知道obj
里面有个data
), 但这就是正确的代码 就是这样来使用的;
new
new T
equals to new T[1]
new T[n]
will allocate n
items A1, A2, ..., An
where Ai
is a T
, this operation return a pointer T * ptr
;
new T[n][m]
will allocate n
items A1, A2, ..., An
where Ai
is a T[m]
, this operation return a pointer T (* ptr)[ m]
;
For generality, in new T[a][b][c]...
only a
could be variable, b, c, ...
must be constant;
const
const int a = 123; //< must be initialized
const int b[3] = {1}; //< the same as `constexpr`
You should always remember that, a const/contexpr T
must be initialized (otherwise, it is not const
);
.
Now you can found that, these three elements b[]
are actually not the type const int
, cuz the other two element is not initialized, (you can use is_same
to check it);
type_id
const char * str = typeid( int).name()
获得该类型的底层名称;
@DELI;
typeid( int) = typeid( const int)
, but is_same< int, const int>::value
would be false
, so is_same
is more stronger than typeid
decltype
const_cast
@Delimiter
int a = 1;
const int * p = &a;
int * pp = const_cast< int *>( p);
*pp = 2;
Now, a = *p = *pp = 2
and &a = p = pp
;
That is, a Object (writable) and then given you a Const-Pointer of it, actually we can use this Const-Pointer to modify that object (cuz that object is writable);
.
e.g., the const int * Head;
in the template-algorithm of Linked-List graph, we can use const_cast
to modify the private-data head
, due to head
itself is writable;
--
const int a = 1; //< the same as `constexpr`
const int * p = &a;
int * pp = const_cast< int *>( p);
*pp = 2;
Now, a = *&a = 1
, *p = *pp = 2
, &a = p = pp
;
The reason is not figured out yet, or you can just consider that cuz a
is const
(itself is not writable);
--
const int a[3] = {1}; //< the same as `constexpr`
Now you can found that, these three element is actually not the type const int
(cuz the other two element is not initialized), of course, these three element can be modified;
class/struct
*this
;
在成员函数里, 有一个隐藏的变量: this
: 表示执行当前函数的类对象的指针 (*this
即是其引用);
@DELI;
初始化列表构造临时对象;
class N{
int a;
string b;
};
The code ST a = { 123, "bb"}
is wrong;
There is a special usage: ST a = { .a = 123, .b = "bb"}
;
@DELI;
const
与constexpr
成员变量;
class ST{
ST( int _a)
:
a( _a){
}
const int a;
static constexpr int A = 123;
};
constexpr
variables in class must be static
; so A
is unique for ST
(all objects of ST
has the same unchangeable A
; it must be initialized when declaring it which is also the unique way for assigning a constexpr
variable; (the value of it is definite before the code-execution)
const
variables is unique for a specific object of ST
, not for ST
; (ST t1
where t1.a = 1
, while t2.a = 2
in another object), and the value of a const
variable can be assigned by user with any custom value;
@DELI;
成员函数是friend
友元的;
class ST{
ST Get_deepCopy() const;
private:
int data;
};
ST ST::Get_deepCopy() const{
ST ret;
ret.data = 123; //< 可以直接访问并修改其私有变量;
return ret;
}
@DELI;
构造函数内的静态变量;
class ST{
public:
ST(){
static int a = 0;
++ a;
}
};
ST a; ST b;
finally,
a
a
a equals
2
2
2;
@DELI;
强制单例类报警;
class ST{
ST(){
bool f = true;
ASSERT_( f);
f = false;
}
};
But one exceptional case is that, if the class has Template ST<?>
, then there can be multiple objects for ST
, (i.e., a singleton-object of ST<1>
, another for ST<2>
, …);
If you wanna there must be one-object for ST
not ST<?>
, then you should forbid the Template for the class.
类
汇总
能使用this
的唯一域 只有成員函數;
@IF(const
成員函數): this, *this
對應當前對象的const
指針/引用;
@ELSE(非const
成員函數): this, *this
對應當前對象的非const
指針/引用;
@DELI;
ST a = b
, 其实他等价于 ST a( b)
(也就是他调用的是*?构造函数*, 跟赋值运算符没有关系);
默认构造函数
拷贝构造函数
深拷贝, 即如果有指针 则需要开辟新空间
int * arr;
ST( const ST & _t){
this->arr = new int[ Len];
memcpy( this->arr, _t.arr, sizeof( int) * Len);
}
移动构造函数
浅拷贝, 即如果有指针 直接指向原内存即可;
.
因为所谓移动 即右值, 他表明 用户不再使用这个对象了, 你可以让新对象 免去申请内存的操作 直接就用原来的内存即可; 但要注意 要把原来对象的指针 置为nullptr
这样当他析构时 不会出错;
int * arr;
ST( ST && _t){
this->arr = _t.arr;
_t.arr = nullptr; // 别忘了这里, 否则当`_t`析构时 就出错了;
}
拷贝赋值运算符
void operator=( const ST & _t){
}
移动赋值运算符
void operator=( ST && _t){
}
数组
子数组的类型;
T A[ 3][ 4][ 5]
A
的类型 即T[3][4][5]
;
A[?]
的类型 为T[4][5]
;
.
也就是, sizeof( A[?]) = sizeof(T) * 4 * 5
;
A[?][?]
的类型 为T[5]
;
.
比如 sort( A[x][y], A[x][y] + 5)
就是让A[x][y][ ...]
的这5个元素排序;
@DELI;
{数组元素的类型};
int A[10];
, A[0]
的类型 不是int
而是int &
;
因此, 如果你要获取该数组元素的类型 必须是remove_reference_t< decltype( A[0])>
;
@DELI;
{数组的类型, 数组指针的类型}
constexpr int N = ?, M = ?;
T a[ N], b[ N][ M];
auto t1 = a;
auto t2 = &a;
auto t3 = b;
auto t4 = &b;
a
的类型 (即t1
的类型) 为: T *
;
&a
的类型 (即t2
的类型) 为: T (*)[N]
;
b
的类型 (即t3
的类型) 为: T (*)[M]
; (注意是[M]
, 不是[N]
, 第一维度变成了指针);
&b
的类型 (即t4
的类型) 为: T (*)[N][M]
;
t1[ i]
等价于 a[ i]
等价于 (*t2)[ i]
;
t3[ i][ j]
等价于 b[ i][ j]
等价于 (*t4)[ i][ j]
;
指针
@DELI;
动态申请二维数组
If you need 2-Dimension Array
[
m
]
[
n
]
[m][n]
[m][n] where m, n
are both variables, here are some devices:
+
T * * arr = new T * [m];
for( int i = 0; i < m; ++i){
arr[ i] = new T[ n];
}
Note that, you have allocated T: n*m
and T*: m
, that is, you have additional T*: m
, this is bad in Efficiency.
+
You need estimate the maximum of m
, suppose it is M
;
T (* arr)[ M] = new T[ n][ M];
This is good in Efficiency, but you always allocate T: n*M
which are greater than n*m
;
@DELI;
宏
宏定义的参数可以含有标点符号;
#define F_( _x) cout<< _x
The code F_( "," << ',' << 1)
is valid, which would becomes cout<< "," << ',' << 1
That is, @Par( _x) can contains any punctuation but excepts a Non-String Comma;
e.g., F_( 1, 2)
is wrong, cuz there has one Non-String Comma that implies the macro F_
should own two parameters (it’s definition should be F_( a, b)
)
If we wanna using macro to replace this code auto wth = g.Head[0]
and auto wth = g->Head[0]
simultaneously.
A wrong macro is F_( _g) auto wth = _gHead[0]
where _gHead
would be viewed as a @Par, so there is a @Par named _gHead
;
The correct device is F_( _g) auto wth = _g Head[0]
@DELI;
##
字符拼接;
#define F_( _a, _b) int _a##_b = 213
Then F_( abc, 12)
equals to int abc12 = 213
@DELI;
#
字符串化;
#define F_( _a) #_a
The code auto ret = F_( a 1 * < ! hh abc 4);
equals to const char * ret = "a 1 * < ! hh abc 4"
;
@DELI;