1.问题提出
类中包含CList模板类,且CList的元素是派生自CObject的自定义类时;编译器报错信息“error C2248:“CObject::operator=”无法访问private成员(在“CObject”类中声明)”
2.问题分析
原因是与C++类的“复制构造函数”与“复制操作符”有关。对于一个C++类来说,需要指定创建、复制、赋值和撤销该类型的对象时会发生什么?这是通过定义特殊的成员函数-构造函数、复制构造函数、赋值操作符和析构函数来实现的。如果类没有显式定义这些函数,则会使用默认函数。通过MFC产生的派生自CObject类或其派生类的自定义类,向导都会自动产生构造函数和析构函数,因此大家对这两个函数是比较熟悉的。而另外两个特殊函数复制构造函数和复制操作符则没有自动生成,如果在类中没有显式定义这两个函数,则类会使用默认复制构造函数和默认赋值操作符。
默认赋值操作符的功能是对类的所有非静态成员变量进行赋值处理。对于类B,如果没有显式的定义其赋值操作符,则类会使用默认的赋值操作符,对CList<A,A&>类型的b进行赋值,此时会调用类型A的赋值操作符,而类A也没有定义复制操作符,因此会调用其父类的赋值操作符,直到调用CObject类的赋值操作符“=”;而CObject类的赋值操作符的访问权限是private,因此编译器报错。
3.问题解决
解决该问题的方法如图中红色部分所示,类A重载赋值操作符,并且将其访问权限设置为public,则类B的默认赋值操作符将会调用类A的重载赋值操作符,而不再去调用类A的默认赋值操作符。
解决方法:在自定义类中重载“=”操作符,并且将其声明为public,即可。
class A:public CObject
{
int x;
A& operator=(const A& a)
{
return *this;
}
};
class B
{
CList<A, A&>b;
4.问题思考
4.1 类B重载赋值操作符,类A是否就可以不用重载赋值操作符了?
当类B重载了赋值操作符,在该操作符中也要对类B的成员变量包含A的CList变量进行赋值,还是需要调用类A的赋值操作符,因此此时类A还是要重载操作符。
4.2 类A要重载赋值操作符,为什么不用重载复制构造函数?
类B的默认复制构造函数此时会调用类A的默认复制构造函数,最后调用CObject的构造函数,而CObject的构造函数访问权限是public,可以访问。所以不需要重载复制构造函数。
4.3 如果类A本身就是基类而没有父类,那么A是否还需要重载复制操作符?
从图中可以看到,如果类A本身就是基类,则类B的默认赋值操作符会调用类A的默认赋值操作符,此时流程到此终止,不会向下进行。