assert() 断言
assert()
是个宏,并且作用并非“报错”。assert
的作用:是先计算表达式 expression
,如果其值为假(即为0),那么它先向 stderr
打印一条出错信息,然后通过调用 abort
来终止程序运行。
#include "assert.h"
void assert( int expression );
assert
的缺点:频繁的调用会极大的影响程序的性能,增加额外的开销。
程序一般分为 DEBUG(调试)和 RELEASE(发行)版本。Debug 版本用于内部调试,Release 版本发行给用户使用。断言assert是仅在Debug 版本起作用的宏,它用于检查“不应该”发生的情况。在调试结束后,可以通过在包含#include 的语句之前插入 #define NDEBUG 来禁用assert调用
右值引用
1、左值:可寻址的、引用的是某一个对象、可位于赋值运算符的左边、可以被赋值。
2、右值:可读的、引用的是某一个对象指向的地址中的数据、通常位于赋值运算符的右边、取出值赋给其他变量。
3、右值引用(T &&)
右值引用就是必须绑定到右值的引用,通过&&来获得右值引用;
声明一个右值引用:int &&a = 0
说明:
a、右值引用所引用的临时对象可以在该临时对象被初始化之后再次修改(这是为了允许move语义),即在右值引用被初始化后,可以使用左值或者左值引用改变右值引用所引用的临时对象的值。
b、能使用右值引用初始化给左值引用(即左值引用绑定到右值引用指向的对象),不能用左值或者左值引用初始化右值引用。
c、C++11使用move函数,用来获取绑定到左值或者左值引用上的右值引用。
move函数
std::move() 函数将一个左值强制转化为右值引用,以用于移动语义。
移动语义,允许直接转移对象的资产和属性的所有权,而在参数为右值时无需复制它们。
换一种说法就是,std::move() 将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。
因此,通过std::move(),可以避免不必要的拷贝操作。
#include <iostream>
#include <utility>
#include <vector>
#include <cstring>
#include <cstdlib>
class CString{
public:
//普通构造函数
CString()
{
std::cout << "...init construct..." << std::endl;
str = (char*)(new char[strlen("")+1]);
*str = '\0';
}
//普通构造函数
CString(char value[])
{
std::cout << "...construct..." << std::endl;
str = NULL;
int len = strlen(value);
str = (char *)malloc(len + 1);
memset(str, 0, len + 1);
strcpy(str, value);
}
//拷贝构造函数
CString(const CString &rhs)
{
std::cout << "...copy construct..." << std::endl;
str = NULL;
int len = strlen(rhs.str);
str = (char *)malloc(len + 1);
memset(str, 0, len + 1);
strcpy(str, rhs.str);
}
//移动构造函数
CString(CString &&rhs)
{
std::cout << "...move construct..." << std::endl;
str = NULL;
str = rhs.str;
rhs.str = NULL;
}
//移动赋值函数
const CString& operator=(CString &&s)
{
std::cout << "move operator=..." << std::endl;
if (str != NULL) {
free(str);
str = NULL;
}
str = s.str;
s.str = NULL;
return *this;
}
//析构函数
~CString()
{
std::cout << "...析构函数..." << std::endl;
if (str != NULL)
{
free(str);
str = NULL;
}
}
public:
char *str;
};
int main()
{
char strValue[] = "test";
CString str(strValue);// 调用普通构造函数
CString s1(strValue);
CString s2; //调用默认构造函数
//CString s2 = std::move(s1);//调用移动赋值函数
std::vector<CString> vecStr1;
vecStr1.push_back(str);//调用拷贝构造函数
std::cout << vecStr1[0].str << std::endl;
if (str.str != NULL){
std::cout << str.str << std::endl;//运行会打印
}
std::vector<CString> vecStr2;
vecStr2.push_back(std::move(str));//移动构造函数(传递一个右值引用)
std::cout << vecStr2[0].str << std::endl;
if (str.str != NULL){
std::cout << str.str << std::endl;//运行不会打印
}
}
移动构造函数的参数和拷贝构造函数不同,拷贝构造函数的参数是一个左值引用,但是移动构造函数的参数是一个右值引用。这意味着,移动构造函数的参数是一个右值或者将亡值的引用。也就是说,只有用一个右值,或者将亡值初始化另一个对象的时候才会调用移动构造函数。而move语句,就是将一个左值变成一个将亡值。
移动赋值函数的参数和普通赋值函数不同,移动赋值函数的参数是一个右值引用。
说明:拷贝构造函数的调用时机:形实结合、函数返回值、使用一个对象出初始化另一个对象。移动构造函数的调用时机:使用右值或者将亡值(对左值或者左值引用使用move函数取得将亡值)初始化另一个对象。
移动赋值函数的调用时机:使用右值或者将亡值赋值给另一个对象