目录
<一>c++智能指针shared_ptr跟unique_ptr针对析构函数调用区别
结论:
shared_ptr:
无论基类的析构函数是否虚化,释放对象时基类跟子类的析构函数都会调用.
unique_ptr:
基类析构函数虚化,释放对象时基类跟子类的析构函数都会调用.
基类析构函数未虚化,释放对象时只会调用基类的析构函数,子类析构函数不会被调用.
代码:
代码:
#include <iostream>
#include <string>
#include <memory> // 共享指针必须要包含的头文件
using namespace std;
class A {
public:
A(){
cout<<"A=="<<endl;
}
virtual void fun() = 0;
virtual ~A(){ // 是否虚化决定unique_ptr指针在调用析构函数是否调用子类析构函数
cout<<"~A--"<<endl;
}
};
class B:public A{
public:
B(){
cout<<"B=="<<endl;
}
void fun(){
cout<<"B - fun()"<<endl;
}
~B(){
cout<<"~B--"<<endl;
}
};
int main(){
// std::shared_ptr<A> sa(new B());
// sa->fun();
std::unique_ptr<A> ua(new B());
ua->fun();
return 0;
}
结果:
// 基类析构函数未虚化
shared_ptr:
[root@local mydir]# g++ -g zhineng_ptr.cpp -std=c++11
[root@local mydir]# ./a.out
A==
B==
B - fun()
~B-- [调用了子类的析构函数]
~A--
unique_ptr:
[root@local mydir]#
[root@local mydir]#
[root@local mydir]# g++ -g zhineng_ptr.cpp -std=c++11
[root@local mydir]# ./a.out
A==
B==
B - fun()
~A-- [仅仅调用基类的析构函数]
//基类析构函数虚化
shared_ptr:
[root@local mydir]# g++ -g zhineng_ptr.cpp -std=c++11
[root@local mydir]# ./a.out
A==
B==
B - fun()
~B--
~A--
unique_ptr:
[root@local mydir]#
[root@local mydir]# g++ -g zhineng_ptr.cpp -std=c++11
[root@local mydir]# ./a.out
A==
B==
B - fun()
~B--
~A--
原因:
<二>C++Lamda表达式
a.表达式语法结构:
lamda表达式函数会自动推到返回值无需编写,带返回值的写法
// lamda表达式函数会自动推到返回值无需编写,带返回值的写法
auto pFunRet = []() -> std::string {
std::string s("rrrrr");
return s;
};
std::cout<<"pFunRet() = "<<pFunRet().c_str()<<std::endl;
b.无参表达式
// 无参Lamda函数表达式
auto pFun = [](){
std::cout<<"This is lamda fun"<<std::endl;
};
pFun();
c.有参表达式
// 带参数
auto pFunParams = [](int a,std::string s){
std::cout<<"Params lamda="<<a<<"-"<<s.c_str()<<std::endl;
};
pFunParams(100,"LX");
d.值捕获(&附加说明)
值捕获,捕获作用域所有值,=可以替换成某个具体的值,表示单个变量捕获
// 值捕获,捕获作用域所有值,=可以替换成某个具体的值,表示单个变量捕获
int val_0 = 1;
int val_2 = 2;
auto pFunValue = [=](){
// 值捕获都是只读权限,强行改变会编译报错,提醒read-only 'val_0'
// val_0 += 1;
// val_2 += 2;
std::cout<<"val_0="<<val_0<<" val_2="<<val_2<<std::endl;
};
pFunValue();
mutable 修饰等价于函数内部做了一份拷贝,之后再对拷贝进行操作,故而可以修改其捕获value2,
// 值捕获2
int value2 = 100;
std::cout<<"before modify value2="<<value2<<std::endl;
auto pFunValue2 = [value2]() mutable {
// mutable 修饰等价于函数内部做了一份拷贝,之后再对拷贝进行操作,故而可以修改其捕获value2,如下输出结果展示:
// before modify value2=100
// value2=211
// after modify value2=100
value2 += 111;
std::cout<<"value2="<<value2<<std::endl;
};
pFunValue2();
std::cout<<"after modify value2="<<value2<<std::endl;
e.引用捕获
引用修改的是捕获的变量的本身,如下输出结果:
// 引用捕获
int quote = 100;
std::cout<<"before modify quote="<<quote<<std::endl;
auto pFunQuote = ["e](){
quote += 20; // 引用修改的是捕获的变量的本身,如下输出结果:
//before modify quote=100
//quote=120
//after modify quote=120
std::cout<<"quote="<<quote<<std::endl;
};
pFunQuote();
std::cout<<"after modify quote="<<quote<<std::endl;
return 0;
<三>sort方法的排序
sort(v.begin(),v.end()) // 默认生序排序
sort(v.begin(),v.end(),cmp)// 排序根据自定义的cmp排序方式。
c++11中使用对应的模版:
greater<int>【生序】,less<int>两种模版进行排序【降序】
sort(v.begin(),b.end(),comp()) //comp后面必须跟括号,因为这里使用的是函数对象,仿函数
struct comp{
bool operator(int & a,int b){
return a < b;
}
};
2.利用数据流进行类型之间转换,一般使用整形跟字符串使用比较多,另外系统库自带to_string转字符串。
template<typename out_type, typename in_type>
out_type convert(const in_type & in_obj)
{
std::stringstream ss;
ss << in_obj;
out_type out_obj;
ss >> out_obj;
return out_obj;
}
3.c++中宏定义重的#跟##的用法
#:字符串拼接
// 预编译时,将传递的参数拼接成字符串的形式
#define CONVERT(name,DDD) #name#DDD
// int main(int argc, char* argv[])
// {
// printf("You and %s are friends.\n", CONVERT(ll,dddd));
// return 0;
// }
输出:lldddd
------------------------------------------------------------
##: 用于拼接代码块
// ## 主要用于拼接代码如下;
class Obj{
public:
Obj(){}
~Obj(){}
void fun(){std::cout<<"Obj++"<<std::endl;}
};
#define NEW_OBJ(obj) \
std::shared_ptr<Obj> _ptr_##obj##__(new Obj()); \
auto &obj = *_ptr_##obj##__;
int main()
{
NEW_OBJ(test_obj);
test_obj.fun();
return 0;
}
输出:
Obj++
注:以上方式,在公共方式合适的地方使用会有好的效果,避免过多滥用,不然导致代码为户型非常差。
4.利用io数据流进行类型拆分转换
a.拆分字符串并转化成整形List,相互转化
#include <iostream>
#include <istream>
#include <sstream>
#include <vector>
#include <string>
using namespace std;
void Str2UintList(const string &str,vector<int> &vec) {
if(str.empty()){
return;
}
istringstream ss(str);
int tmp = 0;
char delim = 0;
while(ss >> tmp){
vec.push_back(tmp);
ss >> delim;
}
}
void UintList2Str(string &str,const vector<int> &vec){
if(vec.empty()){
return;
}
ostringstream os;
for(auto & v : vec){
os << v <<';';
}
str = os.str();
}
int main()
{
string str = "111,222,3333";
vector<int> vec;
Str2UintList(str,vec);
for(int i = 0;i < vec.size();++i)
{
cout<<vec[i]<<endl;
}
string os_str;
UintList2Str(os_str,vec);
cout<<os_str.c_str()<<endl;
return 0;
}
<四> c++11几个特性提升编码效率
(1),=delete :用来修饰一些函数默认不自己生成(比如拷贝构造,赋值函数)
// 初始化&delete用法:
class A{
public:
A():arr{0} //
{}
//注解标签
// [[deprecated("use funcY instead")]] void funcX()
// {
// //实现省略
// }
A(const A& a) = delete;
A& operator =(const A& a) = delete;
private:
int arr[10];
std::string s{"lx"};
bool flag{true};
};
(2) =default :
如果一个C++类没有显式给出构造函数、析构函数、拷贝构造函数、 operator= 这几类函数的实现,则在需要它们时,编译器会自动生成;或者,在给出这些函数的声明时,如果没有给出其实现,则编译器在链接时会报错。如果使用=default标记这类函数,则编译器会给出默认的实现。
(3)override:
用于编译检测继承类函数重载(避免因子类重写跟父类函数有差异从而导致不能实现多态机制)
多线程实现:
std::thread、std::mutex、std::condition_variable 少数几个线程同步对象即可,进行多线程编程.
(4) c++11左右值
左值:通常=左边的值成为左值,且可以取地址,有名称,如下:
int a; // 左值
int & b = a; // 左值引用左值
const int & c = a; // 左值引用
左值得引用 就是一般引用,一根使用&变量名表示:
int a = 111;
int &b = a; // 取得a的引用
右值:赋值运算符=右边的值成为右值.
int t = fun();// fun() 为右值
int a;
int b;
int c = a + b; // a+b右值
char *p ="abc"; // "abc" 为右值
右值引用使用&&表示右值引用:
int && a = fun(); // a就是右值引用,fun()返回就是一个右值
**{ 右值引用也相当于别名,与左值的区别为右值引用是无名变量的别名。
fun() 是一个返回右值的函数,右值在这一句执行完就该结束他的生存期了,如果是对象就该调用析构函数 了;但是使用右值引用,从而右值引用指向右值,右值的生命周期和右值引用通等了, 相比直接赋值少一次对象的析构和构造}.
右值引用减少对象的拷贝,从而提升系统的性能.
右值引用主要有两个用处,一个是移动语义,一个是完美转发。
a>移动语义
C++11中引进的一种move语义,作用是在一些对象构造是已经分配的资源,再其他对象使用时,不需要通过拷贝,重新申请内存,通过移动而非拷贝将对应的资源进行接管使用。
移动构造函数,使用的是&&表示:
// 移动构造
A(A && a):p(a.p){
cout<<"A(A && a) = "<<"move"<<endl;
}
例如移动拷贝使用:
#include <iostream>
using namespace std;
class A {
public:
A():p(new int[5]){
cout<<"A() = "<<"default - construct = "<<endl;
}
A(A & a):p(new int[*a.p]){
cout<<"A(A & a) = "<<"copy - construct"<<endl;
}
// 移动构造
A(A && a):p(a.p){
cout<<"A(A && a) = "<<"move"<<endl;
}
~A(){
cout<<"~A()= "<<"destory"<<endl;
delete p;
}
int * p;
};
A get(){
return A();
}
int main(){
cout<<"1............"<<endl;
A a;
cout<<"2............"<<endl;
A b(a); //copy
cout<<"3............"<<endl;
A c = a; //copy
cout<<"4............"<<endl;
A d = get(); //两个移动构造调用,
// 右值引用,返回一个局部变量对象,此时通过移动构造进行构造d
// getM函数返回一个对象,此时临时对象是右值,调用一次移动构造,根据开启临时对象
// -fno-elide-constructors 该参数关闭编译器省略复制构造优化
// getM()返回前,A()先移动构造一个临时对象,再者通过临时对象再调用移动构造初始化d
/*
[root@localhost c++]# ./a.out
1............
A() = default - construct =
2............
A(A & a) = copy - construct
3............
A(A & a) = copy - construct
4............
A() = default - construct =
A(A && a) = move
~A()= destory
A(A && a) = move
~A()= destory
5............
A(A && a) = move
~A()= destory
~A()= destory
~A()= destory
~A()= destory
~A()= destory
[root@localhost c++]#
*/
cout<<"5............"<<endl;
A f(move(a)); //move
return 0;
}
std::move函数:
int a =1;
int & b = a; // 左值引用
int && c = a;// a为一个左值,不用于到右值引用,编译会报错
int && d = std::move(a); // 将左值a变成右值
通过move将左值转化成右值:
int main()
{
std::string s = "Hello"; //Hello一个右值
std::cout << "before s is " << s.c_str() <<endl;
std::string t = std::move(s); // 调用move将s转化成右值
std::cout << "After move, s is " << s <<endl;
std::cout << "Current t is "<<t.c_str()<<endl;
}
输出:
[root@localhost c++]# ./a.out
before s is Hello
After move, s is // s资源转移,已经没法使用了
Current t is Hello
[root@localhost c++]#
b>完美转发:
c++11通过std::forward()函数来实现,对应的完美转发;
如下:
#include <iostream>
#include <utility>
#include <vector>
#include <string>
using namespace std
template<class T>
void fun(T & c){ // 左值引用
cout<<"t = "<<"left - reference"<<endl;
}
template<class T>
void fun(T&& c){// 右值引用
cout<<"t = "<<"right - reference"<<endl;
}
template<class T>
void Uc(T&& c){ //万能引用格式
fun(std::forward<T>(c)); // 保持原样类型进行传递,被称为完美转发
}
int main()
{
int n = 111;
Uc(n); // 左值
Uc(111); // 右值
return 0;
}
<五>c++程序内存模型
如下图:
上图三个申请内存函数的含义: