在鹅场,c/c++才是王道,工作需要,又得拾起c/c++。
一、指针与引用
1. Java简单的地方就是摒弃了指针,一个指针变量存放的内容就是所指对象的地址。(对一个变量求地址,可以使用&)
int main(){
int a = 3;
int *p = &a; //p指向a的地址
int **q = &p; //q指向p地址
cout <<a<<" "<<*p<<" "<<&a<<" "<<p<<" "<<&p<<" "<<q<<endl;
return 0;
}
输出如下:
3 3 0x28ff18 0x28ff18 0x28ff14 0x28ff14
指针存放的是地址,所以p存放的就是a地址,*p就是指向地址的内容,也就是0x28ff28,直接输出p,则输出a的地址,也就是0x28ff28,&p就是输出p的地址,所以q的存放内容也就是p的地址0x28ff24
2. 引用,在很多Java的经典书籍中,我们常常看到reference,大多翻译成引用,C++的引用与Java还是有所不同的,C++的引用可以理解成就是变量别名,Java的引用可以改变指向的实例,特别是在函数传参的时候,对于基本类型,Java是直接拷贝变量值传参,对于对象都是通过引用拷贝(跟C++中引用不同)传参,通过应该引用能够更改其指向的对象内部值,但是更改该引用值,仅对函数内部可见,函数外部的实参依然没有改变。
C++的引用需要在初始化就指定引用的对象,并且不可变换。
如果我们想要swap两个变量,对于Java来说,如果变量是基本类型,不好意思,做不到。对于是对象类型,则可做到。
但C++不同,有了引用这一强大的功能,可以轻易做到,先说说C版本通过指针交换的版本:
void swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
};
有了引用机制,以上程序的C++版本可以为:
void swapInt(int &a, int &b){
int temp = a;
a = b;
b = temp;
}
调用如下:
int main() {
int a = 10;
int b = 20;
swapInt(a,b);
cout<<a<<" "<<b<<endl; //输出20 10
return 0;
}
二、const
const 这个是Java的关键字,但只做保留,目前没有定义使用,Java如果要定义属性不可修改可以使用final 关键字,但C++这里使用的是const,在上面的swap函数中,我们知道定义C++函数参数,如果不是引用的话,都是深复制参数,不像Java,比如下面代码段(
这里函数参数并不会深复制,都是形参指向):
public static void swapInteger(Integer a, Integer b){
Integer temp = a;
a = b;
b = temp;
}
public static void main(String[] args) {
Integer a = new Integer(5);
Integer b = new Integer(10);
swapInteger(a,b);
System.out.println(a + " " + b);//输出 5 10
}
但是C++,如果你定义的函数参数不是引用类型的话,是会自动调用拷贝构造函数来浅复制一个新对象来作为函数参数的,比如下面代码:
class ClassA
{
public:
ClassA(){
cout<<"构造函数"<<endl;
}
ClassA(const ClassA &a){
cout<<"拷贝构造函数"<<endl;
}
~ClassA()
{
cout<<"析构函数"<<endl;
}
void setI(int i){
this->i = i;
}
int getI(){
return this->i;
}
private:
int i = 0;
};
//参数a是采用ClassA的拷贝构造函数来实例一个对象,然后作为参数的。
void test(ClassA a){
cout<<a.getI()<<endl;
}
int main() {
ClassA a1;
a1.setI(5);
test(a1);
return 0;
}
最终输出:
构造函数
拷贝构造函数
0
析构函数
析构函数
以下几种情况都会调用拷贝构造函数:
1.一个对象以值传递的方式传入函数体
2.一个对象以值传递的方式从函数返回
3.一个对象需要通过另外一个对象进行初始化
三、深复制与浅复制
Java的深复制,一般都是通过implements Cloneable 覆盖clone方法来实现深复制。
一般的变量赋值,比如下面代码都是浅复制,也就是直接引用:
A a = new A();
A a1 = a; // a1和a都是指向同个实例,对a的修改,也就是对a1的修改
C++的复制,归于拷贝构造函数,在上面的说明中,我们知道有三种情况会使用到拷贝构造函数。
C++与Java一样,如果我们没定义构造函数,编译器会自动帮我们构造一个无参数的构造函数,但是如果我们定义了构造函数,那么编译器将不会构造无参构造函数。
重新定义拷贝构造函数,可以为我们做到更多,比如深复制,而不是使用编译器自己的拷贝规则:基本类型深复制,类类型使用其拷贝构造函数来拷贝。下面用代码举个例子:
class ClassA
{
public:
ClassA();
~ClassA();
void setI(int i);
void setName(std::string& n);
std::string getName() const;
int getI();
private:
int i;
std::string *name;
};
上述类ClassA并没有定义拷贝构造函数,所以拷贝规则为:int类型深复制,string *类型也是直接复制存放的地址。
int main() {
ClassA a1;
string hello("hello");
a1.setName(hello);
a1.setI(5);
ClassA a2 = a1;//拷贝构造
hello.append(" world");
a1.setI(10);
cout<<a2.getI()<<" "<<a2.getName()<<endl;
cout<<a1.getI()<<" "<<a1.getName()<<endl;
return 0;
}
输出:
构造函数ClassA
5 hello world
10 hello world
析构函数ClassA
析构函数ClassA
上述例子代码中,a2和a1的name属性都是执行同一个string实例,所以对实例的修改,都会生效。但是i属性则是不同的内存空间,所以a1对i属性的修改并不会生效到a2实例。