一切皆可以用列表初始化。
struct Point
{
int _x;
int _y;
};
class A
{
public:
//explicit A(int x, int y)
A(int x, int y)
:_x(x)
,_y(y)
{}
A(int x)
:_x(x)
,_y(x)
{}
private:
int _x;
int _y;
};
//int main()
//{
// // c
// int array1[] = { 1, 2, 3, 4, 5 };
// int array2[5] = { 0 };
// int array3[5]{ 0 };
//
// Point p = { 1, 2 };
//
// // 单参数的隐式类型转换
// A aa2 = 1;
//
// A aa4 = { 1 };
// A aa5 { 1 };
//
// // 多参数的隐式类型转换
// A aa1 = { 2,2 };
//=也可以省略
// A aa6 { 2,2 };
}
x自定义类型 = Y类型(叫做隐式类型转换);x支持y为参数类型的构造函数就可以;
容器想用不固定的{}数据个数初始化,就用initializer_list支持。
右值引用:
左值是⼀个表⽰数据的表达式(如变量名或解引⽤的指针),⼀般是有持久状态,存储在内存中,我 们可以获取它的地址,左值可以出现赋值符号的左边,也可以出现在赋值符号右边。定义时const 修饰符后的左值,不能给他赋值,但是可以取它的地址。
以下都是左值:
右值也是⼀个表⽰数据的表达式,要么是字⾯值常量、要么是表达式求值过程中创建的临时对象 、匿名对象等 ,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。
以下都是左值:
左值引用给左值起别名(Type& r1 = x),右值引用给右值起别名( Type&& rr1 = y)。
左值引⽤不能直接引⽤右值,但是const左值引⽤可以引⽤右值。
const string& ref1 = string("1111");
右值引⽤不能直接引⽤左值,但是右值引⽤可以引⽤move(左值)
string&& rref5 = move(s1);
引用的意义是减少拷贝,提高效率。
完美转发:
右值引用,引用后,右值引用本身属性变成左值,所以使用完美转发std::forward<T>(t)在传参的过程中保持了t的原生类型属性。
void Fun(int& x) { cout << "
左值引⽤
" << endl; }
void Fun(const int& x) { cout << "const
左值引⽤
" << endl; }
void Fun(int&& x) { cout << "
右值引⽤
" << endl; }
void Fun(const int&& x) { cout << "const
右值引⽤
" << endl; }
template<class T>
void Function(T&& t)
{
Fun(t);
//Fun(forward<T>(t));//完美转发:传左值调左值函数,传右值调右值函数
}
c++11新加了两个默认函数:移动构造函数和移动赋值运算符重载(没有实现移动构造,且没有实现拷贝构造,拷贝赋值重载,析构函数三者任意一个则会自动生成)。
lambda(本质是仿函数):
lambda表达式的格式: [capture-list] (parameters)-> return type {function boby }
[capture-list] :捕捉列表
(parameters) :参数列表
->return type :返回值类型
{function boby} :函数体
lambda使用:
auto add1 = [](int a, int b)->int {return a + b; };
cout << add1(1, 2) << endl;
捕捉列表的使用:
//捕捉列表
// auto swap2 = [a, b]() mutable
// {
// int tmp = a;
// a = b;
// b = tmp;
// };
// // 引用方式捕捉
// auto swap3 = [&a, &b]()
// {
// int tmp = a;
// a = b;
// b = tmp;
// };
//int main()
//{
// int a = 1, b = 2, c = 3, d = 4, e = 5;
// // 传值捕捉所有对象
// auto func1 = [=]()
// {
// return a + b + c * d;
// };
//
// // 传引用捕捉所有对象
// auto func2 = [&]()
// {
// a++;
// b++;
// c++;
// d++;
// e++;
// };
// 混合捕捉,传引用捕捉所有对象,但是d和e传值捕捉
// auto func3 = [&, d, e]()
// {
// a++;
// b++;
// c++;
// //d++;
// //e++;
// };
// // a b传引用捕捉,d和e传值捕捉
// auto func4 = [&a, &b, d, e]() mutable
// {
// a++;
// b++;
// d++;
// e++;
// };
// return 0;
//}
模板的可变参数:
C++11⽀持可变参数模板,也就是说⽀持可变数量参数的函数模板和类模板,可变数⽬的参数被称 为参数包,存在两种参数包:模板参数包,表⽰零或多个模板参数;函数参数包:表⽰零或多个函 数参数。
template <class ...Args> void Func(Args... args) { }
代码示例:
template <class ...Args>
void Cpp_Printf(Args... args)
{
// 计算参数包的数据个数
cout << sizeof...(args) << endl;
}
int main()
{
Cpp_Printf(1);
Cpp_Printf(1, 'A');
Cpp_Printf(1, 'A', std::string("sort"));//参数是可变的。
return 0;
}
编译时递归推导。
包装器:
function是在头文件<functional>,它的优势就是统⼀类型,
std::function 是⼀个类模板,也是⼀个包装器。 std::function 的实例对象可以包装存储其他的可以调⽤对象,包括函数指针、仿函数、 lambda 等。
用法如下:
#include<functional>
#include<iostream>
using namespace std;
int f(int a, int b)
{
return a + b;
}
struct Functor
{
public:
int operator() (int a, int b)
{
return a + b;
}
};
int main()
{
//不是定义可调用对象,而是包装可调用对象,也需要定义对象,例如fc1,fc2,fc3。
// 返回值类型 形参类型
function<int(int, int)> fc1 = f;//包装函数指针
function<int(int, int)> fc2 = Functor();//包装仿函数
function<int(int, int)> fc3 = [](int a, int b) {return a + b; };//包装lambada
cout << fc1(1, 2) << endl;
cout << fc2(1, 2) << endl;
cout << fc3(1, 2) << endl;
return 0;
}
bind用法:
auto f5 = bind(&Sub::sub, &sub, placeholders::_1, placeholders::_2);
cout << f5(10, 5) << endl;
//placeholders::_1是第一个参数,placeholders::_2是第二个参数
//也可以将某个参数绑死。函数只传两个参数,
placeholders::_1仍是第一个参数,placeholders::_2是第二个参数
auto f6 = bind(fx, "王昭君", placeholders::_1, placeholders::_2);
f6(80, 20);
智能指针:
unique_ptr<A[]> sp1(new A[10]);//A是自定义类型
share_ptr<int> sp2(new int[10])
share_ptr(共享指针)智能指针的实现:
pcount统计访问该资源的个数。
template<class T>
class shared_ptr
{
public:
shared_ptr(T* ptr)
:_ptr(ptr)
, _pcount(new atomic<int>(1))
{}
// sp2(sp1)
shared_ptr(const shared_ptr<T>& sp)
:_ptr(sp._ptr)
, _pcount(sp._pcount)
{
(*_pcount)++;
}
void release()
{
if (--(*_pcount) == 0)
{
// 如果是最后一个管理的对象,释放资源
delete _ptr;
delete _pcount;
}
}
// sp1 = sp3;
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
//if (this != &sp)
if (_ptr != sp._ptr)
{
this->release();
_ptr = sp._ptr;
_pcount = sp._pcount;
++(*_pcount);
}
return *this;
}
~shared_ptr()
{
release();
}
int use_count()
{
return *_pcount;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
atomic<int>* _pcount;//使用指针是因为所有对象都需要访问,可能多个对象访问同一个count。
};
特殊类的设计:
单例模式:全局只有唯一的对象
1.饿汉模式;2.懒汉模式
饿汉模式的缺点:问题1:很多单例类,都是饿汉模式,有些单例对象初始化资源很多,导致程序启动慢,迟迟进不了main函数,问题2: 如果两个单例类有初始化依赖关系,饿汉也无法解决。
// 饿汉模式 : 一开始就(main函数之前)就创建对象
class ConfigInfo
{
public:
static ConfigInfo* GetInstance()
{
return &_sInfo;
}
string GetIp()
{
return _ip;
}
void SetIp(const string& ip)
{
_ip = ip;
}
private://构造私有化
ConfigInfo()
{
cout << "ConfigInfo()" << endl;
}
ConfigInfo(const ConfigInfo&) = delete;//不进行拷贝构造
ConfigInfo& operator=(const ConfigInfo&) = delete;
private:
string _ip = "127.0.0.1";
int _port = 80;
// 声明
static ConfigInfo _sInfo;//创建一个静态对象
};
ConfigInfo ConfigInfo::_sInfo;//静态成员类里面声明,类外面定义。
懒汉模式:
// 懒汉:第一次调用GetInstance时创建单例对象,后面就不需要了。
class ConfigInfo
{
public:
static ConfigInfo* GetInstance()
{
if (_spInfo == nullptr) //双线程设计
{
unique_lock<mutex> lock(_mtx);
if (_spInfo == nullptr) // 线程安全
{
_spInfo = new ConfigInfo;
}
}
return _spInfo;
}
string GetIp()
{
return _ip;
}
void SetIp(const string& ip)
{
_ip = ip;
}
private:
ConfigInfo()
{
cout << "ConfigInfo()" << endl;
}
ConfigInfo(const ConfigInfo&) = delete;
ConfigInfo& operator=(const ConfigInfo&) = delete;
private:
string _ip = "127.0.0.1";
int _port = 80;
//...
static ConfigInfo* _spInfo;
static mutex _mtx;
};
ConfigInfo* ConfigInfo::_spInfo = nullptr;
mutex ConfigInfo::_mtx;
类型转换:
C语言:
隐式类型转换:整形之间 浮点数和整形之间
强制类型转换:指针之间 整形和指针
没有关联类型是不支持转换的
c++:
兼容C的转换用法(隐式和强制)
内置类型 -> 自定义类型
class A
{
public:
A(int a1)
:_a1(a1)
{}
A(int a1, int a2)
:_a1(a1)
, _a2(a2)
{}
operator int()//转int类型需要重载
{
return _a1 + _a2;
}
int get() const
{
return _a1 + _a2;
}
private:
int _a1 = 1;
int _a2 = 1;
};
class B
{
public:
B(const A& aa)//构造函数进行自定义类型之间的转换
:_b1(aa.get())
{}
private:
int _b1;
};
int main()
{
// 内置类型 -> 自定义类型
A aa1 = 1;
A aa2 = {2,2};
// 自定义类型 -> 内置类型
int x = aa1;
cout << x << endl;
// 自定义类型 -> 自定义类型
B bb = aa1;
return 0;
}
c++11提供的:
int i = 1;
// 隐式类型转换 : static_cast
double d = static_cast<double>(i);
int* p = &i;
// 显示的强制类型转换 : reinterpret_cast
int address = reinterpret_cast<int>(p);
//const_cast<int*>主要是处理const修饰的对象
const int a = 1;
int* ptr = const_cast<int*>(&a);
(*ptr)++;
class A
{
public:
virtual void f() {}
int _a1 = 1;
};
class B : public A
{
public:
int _b1 = 1;
};
void fun(A* pa)
{
// pa指向B对象,转换成功
// pa指向A对象,转换失败,返回空
B* pb = dynamic_cast<B*>(pa);//前提是父类必须是虚函数
if (pb)
{
cout << pb << endl;
pb->_b1++;
}
else
{
cout << "转换失败" << endl;
}
}