这里写目录标题
- Ⅰ.内部类
- 【特点】
- 1.天生友元
- 2.直接访问static成员
- 3.访问限制符限制
- 4.外部类的大小
- Ⅱ.匿名对象
- 【特点】
- 1.一行生命域
- 2.对象具有常性
- 3.可强行续命
- Ⅲ.拷贝对象时编译器的优化
Ⅰ.内部类
概念:一个类定义在另一个类内部,这个内部的类就叫做内部类。
内部类是一个独立的类,它不属于外部类,更不能通过外部的对象来访问内部类的成员。外部类和内部类理论上没有什么关系,外部类对内部类没有任何优越的访问限定。
class A
{
public:
class B//B类写在A类里面,是内部类
{
public:
void func(const A& a1)
{
}
};
private:
static int _a;
int x=0;
};
int A::_a = 1;
那内部类有什么特点呢?
【特点】
1.天生友元
内部类天生就是外部类的友元类,内部类可以通过对象参数来访问外部类的成员变量,但是外部类不是内部类的友元。
class A
{
public:
class B//B为内部类,天生是A类的友元
{
public:
void func(const A& a1)//友元可以通过对象参数来访问外部类的成员
{
cout << a1.x << endl;//没有问题
}
};
private:
static int _a;
int x=0;
};
int A::_a = 1;
天生友元这个使用就很灵活了。
在外部类定义的成员变量,内部类可以访问,外部类也可以访问到的。
所以一般当内部类和外部类配合使用时,就将内部类的成员定义到外部类去。
这样的话,内部类可以访问,外部类也可以访问。
比如下面这段代码求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
我们可以通过定义n个sum对象,n个sum对象就会调用n次构造函数。
我们只要在构造函数里进行计算即可,不过要注意的是,计算的变量必须是静态变量才可以。
最后再写一个GetRet函数来获取最后的结果,因为在solution函数里无法直接访问sum对象中的私有结果。
class sum {
public:
sum()
{
_ret += _i;
++_i;
}
static int GetRet()
{
return _ret;
}
private:
static int _i;
static int _ret;
};
int sum::_i = 1;
int sum::_ret = 0;
class Solution {
public:
int Sum_Solution(int n) {
//sum a[n];//调用n次构造函数。
return sum::GetRet();
}
};
这个案例我们就可以使用内部类来改造。
我们将原来sum类中的成员变量写到外面的solution类中去,这样内部类可以使用,外部类也可以直接使用,这样最后就不用再写GetRet函数了,因为solution函数可以访问到_ret结果。
class Solution {
//默认是私有的,如果想让别人访问可以写public
class sum {
public:
sum()//内部类,天生是外部类的友元,可以使用外部类的成员变量,因为为静态成员,所以不需要对象就可以使用
{
_ret += _i;
++_i;
}
};
public:
int Sum_Solution(int n) {
// sum a[n];//调用n次构造函数。
return _ret;//这里可以直接返回——ret因为它就是类的成员变量
}
private:
static int _i;
static int _ret;
};
int Solution::_i = 1;
int Solution::_ret = 0;
2.直接访问static成员
注意内部类是可以直接访问外部类的static成员变量的,因为static成员不需要this指针就可以访问,所以不需要外部类的对象/类名。
class A
{
public:
class B
{
public:
void func(const A& a1)
{
cout << _a << endl;//内部类可以直接访问外部类的static成员
//非常OK,不需要外部类的对象
cout << a1.x << endl;//没毛病
}
};
private:
static int _a;
int x=0;
};
int A::_a = 1;
3.访问限制符限制
内部类可以定义类外部类的public,protected,private,都是可以的,取决于使用者。当想被别人访问的就写在public中,不想被别人使用的就写在private中,所以又分为公有内部类和私有内部类。
为什么会受访问限定符的限制呢?因为内部类定义的是一个类型,而访问限定符就是用来限定不同类型的访问的,不同于友元类,友元是不受访问限定符限制的,那是因为写在类里面的只是声明,就是一张图纸而已。
所以当你有这样的想法时:希望这个类藏到类里面,别人访问不到时,就使用private限定。
class A
{
public://公有内部类
class B
{
public:
void func(const A& a1)
{
cout << _a << endl;
cout << a1.x << endl;
}
};
private:
static int _a;
int x=0;
};
class A
{
private://私有内部类
class B
{
public:
void func(const A& a1)
{
cout << _a << endl;
cout << a1.x << endl;
}
};
private:
static int _a;
int x=0;
};
4.外部类的大小
sizeof(外部类)=外部类,和内部类是没有任何关系的。
class A
{
public:
class B
{
public:
void func(const A& a1)
{
cout << _a << endl;
cout << a1.x << endl;
}
};
private:
static int _a;
int x=0;
};
int A::_a = 1;
int main()
{
A a1;
cout << sizeof(A) << endl;
}
为什么是4呢?
首先我们要知道静态变量是不算进去的,因为静态变量就没有存到对象里,所以它是不算A类的大小的。
然后内部类是不占空间的,因为它在类A中只是声明而已,并没有实例化,定义出对象出来,只有在A类中定义了对象B,这样才可以把B类的大小算进去。
在使用内部类之前记得要指定类域才可以正常使用。
Ⅱ.匿名对象
匿名对象就是没有名字的对象
class A
{
public:
A(int a = 10)
:_a(a)
{
cout << "A(int a = 10)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
A aa1(1);//有名对象
//A aa2(),可不能这样写哈,这样写编译器会无法识别这是在创建对象呢还是在声明一个函数呢,当不给参数时,后面就不要给括号。
A(2);//匿名对象
}
那匿名对象有什么用呢?有什么特性呢?
【特点】
1.一行生命域
匿名对象与有名对象的区别在于其生命周期不同。
1.有名对象—生命周期在当前函数局部域。
2.匿名对象—生命周期就只在当前行。
当我们想直接一次性的调用某个成员函数时,就可以使用匿名对象调用,当想重复调用某个成员函数时,就用有名对象。
class Solution {
public:
int Sum_Solution(int n) {
cout << "Sum_Solution(int n)" << endl;
}
};
int main()
{
Solution().Sum_Solution(10);//匿名对象调用成员函数,一次性的
}
2.对象具有常性
我们知道临时变量是具有常性的,而匿名对象也具有常性。
比如下面这个代码就可以验证出来。
A& a1 = A(1);
匿名对象具有常性,所以不可以用引用。但只要给引用前面加上const就可以了。
3.可强行续命
当我们给引用前面加上const后就可以给匿名对象引用了。而加上引用后就出现了一个神奇的现象:匿名对象的生命周期改变了。
int main()
{
const A& a1 = A(1);//OK
}
什么叫强行续命呢?就是原来匿名的生命周期就只是当前行,进入下一行后就销毁了,但用const引用后延长了匿名对象的生命周期,生命周期就跟引用对象一样长了。
为什么呢?可以这样解释:正常的匿名对象,使用完后,后面没有人要使用了,所以使用完就销毁掉了,而用const引用的对象因为还有一个引用一直在吊着它,它如果销毁了就不合理了,所以生命周期延长跟引用对象一样。
Ⅲ.拷贝对象时编译器的优化
在传参和传值返回的过程中,一些编译器会做一些优化,减少对象的拷贝。
当在一行连续的构造时编译器就会进行优化。
class A
{
public:
A(int a = 10)
:_a(a)
{
cout << "A(int a = 10)" << endl;
}
A(const A& aa)
:_a(aa._a)
{
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa)
{
if (this != &aa)
{
_a = aa._a;
}
return *this;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
A Func1()
{
A aa2;
return aa2;
}
void Func2(A a)
{
}
int main()
{
A aa1 = 2;//一个表达式中,构造+拷贝构造---->优化成构造
Func2(5);//跟上面类似,属于隐式类型转化
//一个表达式中,连续构造+拷贝构造--->优化为直接构造
A aa3 = Func1();//一个表达式中,拷贝+拷贝-->优化成一次拷贝
但对于不是同一行的连续构造,编译器是不会做出处理的。
cout << "----------" << endl;
A aa5;//构造函数
aa3 = Func1();//拷贝函数+赋值运算符重载
//编译器不会进行优化
}