1.下面的结果各是什么?
int i = 0;
i = i++ + 1;
Int a = 0;
a = a++ + 1;
和编译器有关,有的编译器上面输出的结果为2,++是后置++,所以i还是0,然后加1,把结果1赋值给i,i变为1,然后再执行++,所以最终i的结果为2
下面的输出的结果为1,++是我们重载的运算符,返回的值是a的旧值,也就是0,所以结果为1
2.请问下面程序输出d.value的值是多少?
Int& operator++(int)
{
static Int old = * this;
old = * this;
++* this;
return old;
}
int main()
{
Int a(10);
Int b = a++;
Int& c = a++;
c = c + 100;
Int d = a++;
}
static Int old = * this;语句只执行一次,执行完Int a(10);后a.value的值为10,执行完Int b = a++;以后,b.value的值为10,a.value的值为11,执行完Int& c = a++;以后c.value的值为11,a.value的值为12,此时c是old的引用,old的值为11,执行完c = c + 100;以后old的值为111,但是在执行Int d = a++;时,用a.value的值去赋值old,old的值变成了12,最终a.value的值为12
一般情况下不要在函数里面轻易定义静态变量,也不要轻易使用银行用返回,如果当一个函数作为引用返回时,用引用接收必须要有原因,否则不要使用引用接收,最好使用值接收
3.a = a++ + 1的解析过程
Int a = 0;
a = a++ + 1;
//a = a.operator++(0) + 1;//0用于区分调用后置++,不一定是0,只要是int类型就可以
//a = operator++(&a,0) + 1;
//a = operator++(&a,0).operator+(1);
//a = operator+(&operator++(&a,0),1);
//a.operator=(operator+(&operator++(&a,0),1));
//operator=(&a,operator+(&operator++(&a,0),1));
执行完operator=(&a,operator+(&operator++(&a,0),1));中(&operator++(&a,0)以后此时的a为1,然后拿返回的旧的对象(a为0)和1相加后的值为1,然后把相加后的结果赋值给a对象,所以a值从0变成1之后,又用1给a赋值,所以最终a的值为1
4.重载运算符什么时候需要用引用返回?什么时候需要用值返回?
如果需要返回运算符本身(如前置++,a+=b),就用引用返回,如果返回的是将亡值(如后置++,a=b+c),就用值返回
5.构造函数的三个作用
①创建对象
②初始化对象
③对于单参的构造函数可以类型转换
//单参参数
/* explicit */Int(int x):value(x)
{
cout << "Create Int :" << this << endl;
}
//两个参数
Int(int x,int y = 0):value(x+y)
{
cout << "Create Int :" << this << endl;
}
int main()
{
Int a(10);
int b = 100;
a = b;
return 0;
}
先构造Int类型的a,再构造 int 类型的b,然后把 int 类型的b的值赋值给 Int 类型的a,会有一个隐式转换,如果在 Int 的构造函数前面加上 explicit明确 关键字,就不允许隐式转换,必须显示转换,将 a = b; 改为 a = (Int)b; 或者 a = (Int)(200);也是正确的
如果不希望构造函数有隐式转换的能力,可以在构造函数前面加上 explicit 关键字
如果构造函数有两个参数是不可以的,所以构造函数的第三个作用只针对于构造函数只有一个参数,但是如果有两个参数的构造函数有一个默认值参数,也可以认为是一个参数,也是可以的,当然如果三个参数中有两个默认值参数也可以,依次类推
注意:
a = (Int)(b,100); 和 a = Int(b,100);是不一样的,第二个可以编译通过,是调用构造函数,创建一个无名对象,有一个默认值参数,所以可以进行隐式转换,第一个不能编译通过,它是以强转的方式构造了一个无名对象,必须是一个参数才可以
a = b;要想把内置类型(变量)赋值给自定义类型(对象),可以通过单参构造函数来实现
b = a;要想把自定义类型(对象)赋值给内置类型(变量),可以设计一个强转函数来实现,强转函数返回的类型就是强转后类型
operator int () const
{ return value; }
b = a;//隐式转换
//b = a.operator int();
//b = operator int(&a);
b = (int)a;//显示转换
定义了对象a和对象b,a<b对象和对象是不能相比的,所以有一个隐式转换的过程,用对象a的value值和对象b的value值作比较,a<x是用对象a的value值和x作比较,重载了类型强转运算符(把对象强转为某个内置类型,注意:强转后对象并不会变成内置类型,对象还是对象)
operator int() const
{ return value; }
int main()
{
Int a = 10,b = 20;
a < b;
int x = 100;
a < x;
return 0;
}
6.mutable 易变关键字
mutable的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。在C++中,mutable是为了突破const的限制而设置的,被 mutable 修饰的变量,将永远处于可变的状态,即使在一个 const 函数中
我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰
下面举个例子可以通过加mutable关键字改变用const修饰的value的值:
class Add
{
private:
mutable int value;
public:
Add(int x = 0):value(x) {}
int operator()(int a,int b) const
{
value = a + b;
return value;
}
};
int main()
{
int a = 10,b = 20,c = 0;
Add add;
c = Add(a,b);
return 0;
}
c = add(a,b);并不会去调动Add的构造函数,实际上编译器将它看待成c = add.operator()(a,b),add是一个对象,但是它的调动方式相当于函数,它实际上是对象,但是它的调动方式像函数一样,所以把它称之为仿函数,add相当于一个函数名,凡是重载了()运算符,就把它称之为仿函数,所以以后看到c = add(a,b);的时候,add可能是函数名也可能是对象,如果重载了()运算符就是对象
c = Add()(a,b)是什么意思?类型名+()调动构造函数产生一个临时对象(将亡值对象),将亡值对象调动自己的()运算符重载,把a和b的值相加
7.下面程序调动的过程是怎么样的?
class Int
{
private:
int value;
public:
Int(int x = 0):value(x)
{
cout << "Create Int: " << this << endl;
}
Int(const Int& it):value(it.value)
{
cout << "Copy Create Int:" << this << endl;
}
Int& operator = (const Int& it)
{
if(this != &it)
{
value = it.value;
}
cout << this << " = " << &it << endl;
return *this;
}
~Int()
{
cout << "Destroy Int : " << this << endl;
}
};
class object
{
private:
int num;
Int val;
public:
Object(int x,int y)
{
cout << "Create Object:" << this << endl;
num = x;
val = y;
}
~Object()
{
cout << "Destroy Object:" << this << endl;
}
};
int main()
{
Object obj(1,2);
}
①程序从主函数开始执行,执行到Object obj(1,2);的时候系统给obj分配空间,但是此时还没有对象,然后先创建成员对象,调用Int的构造函数,打印出Create Int:xxxx xxxx
②然后调动obj的构造函数创建了对象obj,打印出Create Object:yyyy yyyy,注意在进入obj的构造函数函数体之前要创建出成员变量
②然后进入构造函数内部,先把x的值赋值给num,执行到val = y时因为我们不能直接把整型的值赋值给对象,所以会调动Int的构造函数创建一个临时对象(无名对象),打印出Create Int:zzzz zzzz之后
③再把临时对象赋值给val,会调动赋值运算符重载函数,打印出xxxx xxxx = zzzz zzzz
④当obj的构造函数结束时要调用Int的析构函数,析构临时对象(无名对象),打印出Destroy Int:zzzz zzzz
⑤然后main函数结束时,先析构对象本身,调用obj的析构函数,打印出Destroy Object:yyyy yyyy
⑥然后释放对象的成员对象,调用Int的析构函数,打印出Destroy Int:xxxx xxxx
如果将Object的构造函数改写成下面这种情况,那么函数的调动是什么样的呢?
Object(int x,int y) :num(x),val(y)
{
cout << "Create Object:" << this << endl;
}
①先构造obj的成员val,调用Int的构造函数,打印出Create Int:xxxx xxxx
②然后调用obj的构造函数,打印出Create Object:yyyy yyyy
③然后调动obj的析构函数,打印出Destroy Object:yyyy yyyy
④然后调用Int的析构函数,析构obj的成员对象,打印出Destroy Object:xxxx xxxx
这时候构建就会非常简单,构建对象的次数会很少,所以对类的成员,尽可能的使用初始化列表的方式进行构建,可以使构建对象的次数减少,付出的代价减少
初始化列表变量的构建顺序和声明的顺序一致
8.可以实现自动管理,防止内存泄露
class Object
{
private:
Int* ip;
public:
Object(Int* s = NULL):ip(s)
{
}
~Object()
{
if(ip != NULL)
{
delete ip;
}
ip = NULL;
}
};
int main()
{
Object obj(new Int(10));
return 0;
}
9.组合,一个对象里面包含另一个对象
①值类型组合,这样的组合称之为强关联,强关联是指当产生object对象时,object里面必然包含val对象
class Int {};
class Object
{
int num;
Int val;
};
Object中包含内置类型num和自定义类型val,val这个对象存在于Object中,是Object的一部分
②指针类型组合,这样的组合称之为弱关联,因为我现在指向的时堆区的对象,一会我可能指向堆区的另一个Int类型的对象,智能指针用的多
class Int {};
clas Object
{
int num;
Int *ip;
};
Object中包含内置类型num,自定义类型指针ip,ip指向的是堆区的Int类型的对象,不包含在Object对象中,不是Object的一部分
如何能够显示出指针所指的对象,然后进行相应的操作呢?就要重载两个重要的运算符,解引用运算符*和指向运算符->,解引用运算符直接返回堆区对象,指向运算符直接返回堆区对象的地址
class Object
{
private:
Int* ip;
public:
Object(Int* s = NULL):ip(s) {}
~Object()
{
if(ip != NULL)
{ delete ip; }
ip = NULL;
}
Int& operator*() //返回对象本身
{ return *ip; }
const Int& operator*() const
{ return *ip;}
Int* operator->() //返回对象的地址
{ return &**this; }// *this就是对象obj,然后调用*重载返回对象本身,然后再取对象的地址
const Int* operator->()
{ return &**this; }
};
int main()
{
Object obj(new Int(10));
return 0;
}
operator&是取本对象的地址,operator->是取成员ip所指向的对象地址,是不同的概念
③引用类型组合,我们把这种组合称之为强引用,用的较少,前面两种用的较多
class Int {};
class Object
{
int num;
Int &val;
};
10.因为我们重载了解引用*运算符和指向->运算符,所以对象obj和裸指针具有相同的功能,所以用起来和指针很像,但是它比指针要好一些,原因是当主函数结束的时候,obj要调用析构函数,就把指向的对象释放了,但是对于裸指针,如果忘记delete,就会导致内存泄露,所以用对象的好处是我们只管申请,不需要管它什么时候释放,从而实现自动管理
class Object
{
private:
Int* ip;
public:
Object(Int* s = NULL):ip(s) {}
~Object()
{
if(ip != NULL)
{ delete ip; }
ip = NULL;
}
Int& operator*() //返回对象本身
{ return *ip; }
const Int& operator*() const
{ return *ip;}
Int* operator->() //返回对象的地址
{ return &**this; }// *this就是对象obj,然后调用*重载返回对象本身,然后再取对象的地址
const Int* operator->()
{ return &**this; }// *this就是对象obj,然后调用*重载返回对象本身,然后再取对象的地址
};
int main()
{
Object obj(new Int(10));
Int *p = new Int(1);
(*p).Value();
(*obj).Value();
p->Value();
obj->Value();
return 0;
}