C++复制构造函数,数组与指针,restrict关键字
关于复制构造函数
请思考下面这个列子:
class A{
private:
int value;
public:
A(int n) { value = n;}
A(A other) { value = other.value;}
void Print(){std::cout<<value<<std::endl;}
};
int main(itn argc,char* argv[]){
A a = 10;
A b = a;
b.Print();
}
上面这段程序在main函数中定义实例a通过构造函数完成,然而A类的复制构造函数传参为pass by value,当调用复制构造函数时,把形参复制到实参时又会调用复制构造函数,会形成无休止的递归调用从而导致栈溢出。所以C++的标准不允许复制构造函数传参为pass by value,可以pass by reference。
关于数组与指针
请看下面一个例子:
int GetSize(int data[]){
return sizeof(data);
}
int main(int argc,char* argv[]){
int data1[] = {1,5,1,2,2};
int size1 = sizeof(data1);
int *data2 = data1;
int size2 = sizeof(data2);
int size3 = GetSize(data1);
std::cout<<size1<<" "<<size2<<" "<<size3<<std::endl;
}
该程序输出为:20 4 4。
data1是一个数组,sizeof(data1)求数组的大小。data2为指针,指向数组data1的第一个元素,在32位系统上大小为4个字节。当数组作为函数的参数进行传递时,传递的是数组的首元素的地址指针,所以sizeof()的值为4。
外部变量
外部变量和自动变量类似,与外部变量不同的是,如果未初始化外部变量,它们会自动初始化为0;只能用常量表达式初始化文件作用域:
int x = 10;
int y = 2 + 20;
size_t z = sizeof(int);//正确,只要不是变长数组,sizeof表达式可被视为常量表达式
int x2 = 2 * x;//错误,x是变量。
int main(){
//...
}
restrict 关键字
restrict 关键字允许编译器优化某部分代码以更好地支持计算,只用于指针,表明该指针是访问数据对象的唯一且初始的方式。看以下列子:
int ar[10];
int *restrict restar = (int*)malloc(sizeof(int)*10);
int *par = ar;
这里,指针restar 是访问有malloc()所分配的内存的唯一方式,因此可以用restrict限定它,而指针par既不是访问ar数组的唯一方式也不是初始方式,所以不用它设置为restrict。
现在考虑下面稍微复杂的例子:
for(int n = 0 ; n < 10 ; ++n){
par[n] += 5;
restar[n] += 5;
ar[n] +=3;
par[n] + =3;
restat +=3;
}
由于之前声明了restart是访问它所指向的数组唯一且初始的方式,编译器可以把涉及restart的两条语句替换成下面这条语句,效果相同:
restar[n] += 8;//可以进行替换
但是,如果把与par相关的两条语句替换成下面的语句,将导致计算错误,这是因为for循环在par两次访问相同的数据之前,用ar改变了该数据的值。
restrict 关键字还可以用于函数形参中的指针。这意味着编译器可以假定在函数体内其他标识符不会修改该指针指向的数据,而且编译器可以尝试对其优化.C库中有两个函数用于把一个位置上的字节拷贝到另一个位置上。C99中,这两个函数原型是:
void * memcpy(void* restrict s1,void * restrict s2,size_t n);
void *memmove(void* s1,const void* s2,size_t n);
这两个函数都从位置s2把n字节的数据拷贝到s1上去。memcpy()要求这两个位置不重叠,但是memmove()没有这样的要求。声明s1和s2位restrict说明这两个指针都是访问相应数据的唯一方式,所以它们不能访问相同的块的数据。
restrict 关键字有两个读者。一个是编译器,导致编译器可以自由假定一些优化方案。另一个读者是用户,告知用户要使用满足restrict要求的参数。