一. 函数的原型以及函数创建的时机
C++中创建一个空类:
C++代码
class Empty {};
默认会生成4个函数,其函数的原型如下:
C++代码
public:
Empty() { ... }
Empty(const Empty& rhs) { ... }
~Empty() { ... }
Empty& operator=(const Empty& rhs) { ... }
说明:1) 这些函数只有在需要调用的时候,编译器才会生成。2) 4个函数都是public的。
3) 4个函数都是inline的(即函数定义在类的定义中的函数)。4) 如果你显式的声明了这些函数中的任何一个函数,那么编译器将不再生成默认的函数。
比如,当遇到下列语句时,函数会被编译器生成:
C++代码
Empty e1; //默认构造函数
//对象销毁时,析构函数
Empty e2(e1);//拷贝构造函数
e2 = e1;//赋值运算符
另外,还存在两种默认的函数:就是取地址运算符和取地址运算符的const版本,这两个函数在《Effective C++》中没有提及。
C++代码
public:
Empty* operator&() { ... }
const Empty* operator&() const { ... }
这两个函数是确实存在的,正如下面的代码可以正常工作:
C++代码
#include <stdio.h>
class Empty {
};
int main(int argc, char** argv)
{
Empty a;
const Empty *b = &a;
printf("%p\n", &a); //调用取地址运算符
printf("%p\n", b); //调用const取地址运算符
}
一个容易被忽略的问题:自定义的拷贝构造函数不仅会覆盖默认的拷贝构造函数,也会覆盖默认的构造函数。下面的代码是编译不过的,用户必须再显式的定义一个无参的构造函数。
C++代码
class Empty
{
public:
Empty(const Empty& e) { } //拷贝构造函数
};
int main(int argc, char** argv)
{
Empty a;
}
二. 赋值操作符存在的问题
赋值操作符函数的行为与拷贝构造函数的行为基本是相同的,编译器生成赋值操作符函数是有条件的,如果会产生无法完成的操作,编译器将拒绝产生这一函数。
那么什么时候编译器无法完成赋值这一行为呢?考虑如下情形(来源Effective C++):
C++代码
template<class T>
class NameObject {
public:
NameObject(std::string& name,const T& value);
private:
std::string& nameValue;//引用成员变量
const T objectValue; //const成员变量
};
然后考虑下面的语句会发生什么事:
C++代码
std::string newDog("abc");
std::string oldDog("xxx");
NameObject
NameObject
p = s;//将会发生什么?
赋值语句之前,p.nameValue指向newDog, s.nameValue指向oldDog。那么赋值之后呢?p.nameValue应该指向s.nameValue指向的对象吗?但是C++有一条规定:引用不能改指向另外一个对象。
对于变量objectValue,C++规定:更改const成员是不合法的。
因此如果上面两种情形中的任何一种发生了,C++编译器给出的响应是:拒绝编译这一行的赋值动作。如果你这么做了,C++编译器会报错。
如果你执意要进行赋值操作,那么可以自己定义一个赋值操作符重载函数。
另外如果你想更好的提升你的编程能力,学好C语言C++编程!弯道超车,快人一步!笔者这里或许可以帮到你~
UP在主页上传了一些学习C/C++编程的视频教程,有兴趣或者正在学习的小伙伴一定要去看一看哦!会对你有帮助的~
分享(源码、项目实战视频、项目笔记,基础入门教程)
欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!
编程学习书籍:
编程学习资料: