C++学习之五、理解C++疑难问题
理解C++疑难问题
1. 引用
专业的C++代码都大量使用了引用。C++的引用是另外一个变量的别名。对引用的修改都会改变该引用所指向变量的值。可以把引用看成是一种隐式的指针,它可以免除获取变量地址和对指针解除引用的麻烦。也可以把引用看作是原变量的另一个名字。可以创建独立的引用变量、使用类中的引用数据成员、接受作为传递给函数和方法的参数、从函数和方法返回引用。
(1).引用变量:必须在创建时对其初始化。
int x = 3;
int &xRef = x;
xRef = 10;//x的值修改为10
如果在类外声明的引用变量而不初始化,这是不允许的。
int &yRef; //error
必须在分配引用时对其初始化。通常,引用是在声明是分配的,不过引用数据成员可以在包含该成员的类的初始化列表中进行初始化。
除非引用指向一个const值,否则不能创建指向未命名值的引用。
int &unnameRef = 5 ; //error
const int &unnameRef = 5; ok
引用总是指向初始化时指定的那个变量。一旦创建引用,就不能再修改了。
int x=3,y=5;
int &xRef = x;
xRef = y; //xRef引用没有指向y,仍然是指向x,只是x的值修改成y的值5了。
你或许希望在赋值时取y的地址来绕过这条限制:
int x=3,y=5;
int &xRef = x;
xRef = &y; //error ,y的地址是指针,而xRef是int变量的引用,而不是指针的引用
对于下面会怎么样呢?
int x=3,y=5;
int &xRef = x;
int &yRef = y;
xRef = yRef; //xRef引用没有指向y,仍然是指向x,只是x的值修改成y的值5了。
综上所述:引用在初始化后就不能修改而指向别的变量,只能修改其指向的变量的值。
指针引用和引用指针:
下面是一个指向int指针的引用的例子
int *intP;
int *&ptrRef = intP
ptrRef = new int;
*ptrRef = 5;
注意,取引用的地址和其引用所指向的变量的地址,这二者结果是一样的。
int x =3;
int &xRef = x;
int *xPtr = &xRef;//等价于int *xPtr = &x
*xPtr = 100;
注意:不能声明指向引用的引用,也不能声明引用指针(指向引用的指针)。
int x =3;
int &xRef = x;
int &&doubleRef = xRef;//error
int &*refPtr = &xRef;//error
(2).引用数据成员:
类的数据成员可以是引用。但是如果引用不指向其他某个变量,这样的引用是无法存在的。因此,必须在构造函数初始化列表中初始化引用数据成员,而不是在构造函数体中完成初始化。
(3).引用参数:
C++不常使用独立的引用变量或者引用数据成员。引用最通常的用法是作为函数和方法的参数。
void swap(int& first,int& second)
{
int temp = first;
first = second;
second = temp;
}
而下面的函数达不到效果
void swap(int first,int second)
{
int temp = first;
first = second;
second = temp;
}
我们知道不能使用常量来初始化引用变量,与此类似,不能把常量作为实参传递给采用传引用为参数的函数。
swap(3,5); //error
来自指针的引用:如果把一个指针传递给函数或方法,而该函数或方法需要的是一个引用。在此种情况下,简单地对指针进行解除引用,从而把指针转化为引用。
int x = 3,y=5;
int *xp = &x,*yp = &y;
swap(*xp,*yp);
传值和传引用:
如果想修改参数,并希望这些修改反映到函数或方法的实参变量上,此时就应该采用传引用。但是,不应该限制为只是在这种情况下才采用传引用。传引用可以避免复制函数实参,在某些情况下能带来二个好处:
a. 效率。复制大的对象和结构时可能会花费很长的时间。传引用只向函数或方法传递指向对象或结构的指针。
b. 正确性。不是所有的对象都允许传值。即使允许传值,也不见得就能正确地支持深复制。我们知道要支持深复制,有动态分配内存的对象必须提供定制的复制构造函数。
如果想发挥这二个优点,同时不想改变原来的对象,可以在前面加上const。
传引用的这些优点意味着,对于简单内置类型,不需要修改实参,就应当使用传值。在其他的情况下可以考虑传引用。
(4).引用返回类型:
从函数或方法返回引用。这样做的主要原因是出于效率的考虑。不是返回一个完整的对象,而是从函数或方法返回对象的引用,这样记忆可以避免不必须要的复制。当然,只能当前对象在函数或方法结束仍然存在才可以使用此技术。
注意:必要返回函数或方法中在栈上创建的变量的引用。因为函数或方法在结束时会撤销这些变量。函数中在堆上分配的变量在函数结束时会撤销吗?
(5).采用引用还是指针:
C++中的引用大概是多余的,引用可以做的,几乎指针都可以做。
不过引用比指针编写的代码要清晰一些,也要安全,不可能存在无效的引用,不需要明确地解除引用,所以不会遇到指针可能存在的解除引用错误。
需要改变指着指向的位置的情况下,需要使用指针。
要看参数和返回类型中是采用指针还是采用引用合适,有一种方法,就是考虑谁拥有内存。如果收到变量的代码要负责释放与对象关联的内存,就必须接受对象的指针。如果收到变量的代码不必释放内存,就应该接受变量引用。即,除非需要动态分配内存或者要在其他地方改变或释放指针指向的值,否则,都应当使用引用而不是指针。(这条规则也适用于独立变、函数或方法参数、函数或方法返回值。
关键字疑点:
1.const关键字:
指定或者要求其声明的变量不变。
const有二种不同但相关的用法,一种标识变量,一种标识方法。
a.const变量:声明此变量不能修改。可以把任何变量标识为const,包括全局变量和类的数据成员。也可以使用const来指定函数或方法的参数应该保持不变。
const double PI = 3.14159;//等价于#define PI 3.14159
const指针:
int x = 5;
const int * p = &x; //不能通过指针p来修改x的值,但是可以通过x自身来修改。
*p = 10;//error
x =10; //ok
int const *p = &x; //等价于const int * p = &x
对于:
int x = 5,y = 8;
int *const p = &x;//可以通过p来修改x的值,但是不能修改p指向的对象了。
*p = 10;//ok
p = &y; //error
既然不能修改p本身,所以需要在声明p时对其初始化。
对于:
const int * const p = &x;// 既不能通过指针p来修改x的值, 也不能修改p指向的对象。
b.const 引用:
应用于引用的const关键字通常比应用于指针const关键字要简单。原因有二,一:引用默认就是const的,也就是说不能修改它们指示的变量(即不能让它再指示别的变量)。所以,C++不允许显式地用const来标识引用变量(
补充:软件开发 , C++ ,