1. new后的内存需要释放,经常被人遗忘。有人想到可以利用对象,因为对象生命周期结束后,析构函数将会被调用,可以释放它指向的内存。
而对象的生命周期是程序自动控制的,因此可以轻松防止内存泄漏。
2. 智能指针的析构函数总是释放其指向的堆内存,因此,如果不是动态创建的堆内存,将会发生内存释放错误。
3. 两个指针指向同一块内存,容易发生double free的问题,如何防止?
a 定义赋值运算符,进行深度拷贝
b 建立所有权的转移,例如auto_ptr , unique_ptr(更严谨)
c 创建智能更高的指针,引用计数,赋值时,计数+1,指针过期时,计数-1,仅当最后一个指针过期时才调用delete,这是shared_ptr的概念。
4. auto_ptr<string> p1(new string("auto"));
auto_ptr<string> p2;
p2=p1;
p2接管了p1,这是好事,因为避免double free,但是如果后面访问p1将出错。
如果使用unique_ptr将在编译时出错,如下
unique_ptr<string> p1(new string("auto"));
unique_ptr<string> p2;
p2=p1;
但是,编译器允许将一个临时对象赋给unique_ptr,如下
unique_ptr<string> p3=unique_ptr<string>(new string("unique"));
此外,move() 能将一个unique赋值给另一个,实现所有权转移。如下
unique_ptr<string> p1,p2;
p1=unique_ptr<string>(new string("unique"));
p2=p1;
5. unique_ptr<double[]> p1(new double(5));
auto_ptr和shared_ptr只能用new,而unique_ptr可以用new[]
6. 在unique_ptr为右值时,unique_ptr 可以赋值给shared_ptr
unique_ptr<string> pup(generateU()); ---ok
shared_ptr<string> s1(pup); ----fail, unique_ptr is lvalue
shared_ptr<string> s2(generateU());------ok,, unique_ptr is rvalue
7. c++提供了sstream族,读取string对象中的格式化信息,或将格式化信息写入string对象中---内核格式化。
ostringstream outstr;
string s1("OO");
outstr<<"file format is"<<s1<<"t";
string out = outstr.str();//返回一个被初始化为缓冲区内容的字符串对象。
cout<<out.
string lit = "there is a test file bellow"
istringstream instr(lit);
string word;
while(instr>>word)
{
cout<<word<<endl;
}
8. c++11特性
a) 新类型: long long, unsigned long long, char16_t, char32_t
b) 初始化列表 int x{5}; short quar[3]{1,2,3};
c) 缩窄,初始化列表语法防止缩窄。
char c1=1.57e27-----allow, but undefined behavior
char c1{1.57e27}-----compile error
char c1{66}----allow
d)
auto 申明变量类型
e)
decltype变量类型声明为表达式
decltype(x*n) q; q为x*n的类型
f)
返回类型后置
auto f2(double, int)->double;
g)
nullptr
h)
作用域内枚举
enum class New1{aa, bb, cc};
enum struct New2{aa, bb, cc};
调用时,New1::aa, New2::aa
i)
禁止自动转换
class Pe
{
int mem1 = 0;--------类内成员初始化
Pe(int);
explicit Pe(double);
explicit operator double() const;
};
Pe a, b;
b=0.5;----------fail, 禁止单参数构造函数导致自动转换。
double x = b;----fail, 禁止自动转换。
double x = double(b);---ok
j)
Useless::Useless(const Useless &f):n(f.n)--------拷贝构造函数
{
f的成员赋值给当前对象的成员
}
Useless:Useless(Useless &&f):n(f.n) -----------转移构造函数
{
f的内容复制给当前对象,且f成员内容置空
}
Useless &Useless::operator=(const Useless &f)-----赋值函数
{
f成员赋值给当前对象,深拷贝
}
Useless &Useless::operator=(Useless &&f)---转移赋值函数
{
return *this;
}
k)
如果提供了移动构造函数,编译器不会自动创建默认的构造函数,复制构造函数和赋值函数,在此情况下,可以使用default关键字声明这些默认版本;
class A{
public:
A(A &&);
A()=default;
A(const A&) = default;
A & operator=(const A &)=default;
如果想禁止复制对象,可禁用复制构造函数和赋值运算符
class A
....
A(const A&)=delete;
A &operator=(const A&)=delete;
l)
一个构造函数中使用另一个构造函数---------委托构造函数。
例如派生类构造函数可以调用基类构造函数。
using Base::Base;
Derived(int a, int b):ma(a),Base(b){};
m)管理虚方法
virtual void f() const override;说明您需要覆盖一个虚函数,如果声明与基类方法不匹配,编译器将会报错。如果不使用该关键字,基类同名的方法将被隐藏。
virturl void f() const final;禁止派生类覆盖该方法
n)lambda函数表达式
auto mod3=[](int x){return x%3==0;}; ---------返回类型自动推断
int count = std::count_if(numbers.begin(), numbers.end(), mod3);
仅当表达式完全由一条返回语句组成时才能自动推断返回类型,否则需要显示指定
auto mod3=[¶](int x)->int{para++; return x%3==0;};
o)包装器
double test(double){return double*2.0;}
function<double(double)> e1=test;
p)省略参数
template<typename T, typename...Args>
void show(T value, Args...args)
{
...
}
show(x*x,'!',7,mr);
q)thread_local将变量声明为静态存储,随线程过期而过期。
库支持由原子操作atomic operation库和线程支持库组成
r)pair可以存储两个类型不同值,tuple可以存储任意多个类型不同值
s)regex库为正则表达式库
t)允许共用体成员有构造函数和析构函数