有错请指出,有错请指出,有错请指出,有错请指出,有错请指出,有错请指出,
下面的结论都是在VS下测试的,也许其他编译器会出现不同情况,
- 比如下面这句,在VS下不能编译,但是在其他编译器能编译(华工OJ支持这种写法)
int n = 5;
int a[n];
1. 关于复制构造函数的某个特殊情况,
A g(){ return A();}
A c = g();
- 这种情况,复制构造函数只调用了一次,实际上并没有创建匿名对象,匿名对象直接变成了声明的c,
A c;
c = g();
- 如果是普通的赋值语句,那么它会先调用赋值构造函数来创建匿名对象,在调用”=”重载来进行赋值
g();
- 这个情况就会创建匿名对象,但是在函数结束之和,就会销毁(调用~A());
"测试用代码"
#include<iostream>
using namespace std;
class A{
public:
int a;
A(int c = 0):a(c){
cout<<"**********In A()\n";
cout<<this<<"\n";
cout<<"A:Constructor\n";
cout<<"******Out A()\n";
}
A(A & b){
cout<<"**********In Copy_A()\n";
cout<< & b <<" to "<<this<<"\n";
cout<<"B:Copy_Constructor\n";
a = b.a;
cout<<"**********Out copy_A()\n";
}
~A(){
cout<<this<<"~A\n";
}
A operator = (const A & b){
cout<<"Operator =\n";
a = b.a;
return *this;
}
};
A g(){
cout<<"In g()\n";
A c;
cout<<"before g() return\n";
return c;
}
int main(){
//A c = g();
A c;
c = g();
cout<<"C is:"<<&c<<"\n";
//g();
return 0;
}
2. [2016-04-23][使用复制构造函数的时候,为什么没有调用构造函数来构造临时对象]
- 比如下面这句,在VS下不能编译,但是在其他编译器能编译(华工OJ支持这种写法)
int n = 5;
int a[n];
1. 关于复制构造函数的某个特殊情况,
A g(){ return A();}
A c = g();
- 这种情况,复制构造函数只调用了一次,实际上并没有创建匿名对象,匿名对象直接变成了声明的c,
A c;
c = g();
- 如果是普通的赋值语句,那么它会先调用赋值构造函数来创建匿名对象,在调用”=”重载来进行赋值
g();
- 这个情况就会创建匿名对象,但是在函数结束之和,就会销毁(调用~A());
"测试用代码"
#include<iostream>
using namespace std;
class A{
public:
int a;
A(int c = 0):a(c){
cout<<"**********In A()\n";
cout<<this<<"\n";
cout<<"A:Constructor\n";
cout<<"******Out A()\n";
}
A(A & b){
cout<<"**********In Copy_A()\n";
cout<< & b <<" to "<<this<<"\n";
cout<<"B:Copy_Constructor\n";
a = b.a;
cout<<"**********Out copy_A()\n";
}
~A(){
cout<<this<<"~A\n";
}
A operator = (const A & b){
cout<<"Operator =\n";
a = b.a;
return *this;
}
};
A g(){
cout<<"In g()\n";
A c;
cout<<"before g() return\n";
return c;
}
int main(){
//A c = g();
A c;
c = g();
cout<<"C is:"<<&c<<"\n";
//g();
return 0;
}
2. [2016-04-23][使用复制构造函数的时候,为什么没有调用构造函数来构造临时对象]
就是在
Array operatpr +
return tmp;//Array的某变量
中返回一个对象
然后中途会进入到tmp拷贝到临时对象的复制构造函数中,但是在这个函数里面使用this查看变量,发现this的元素没有初始化,也就是没有调用构造函数(不管是默认的还是复制的)!!!
解决办法:在构造函数里面调用一次构造函数.
3.关于构造函数的调用顺序
- 基类构造函数 —> 成员对象的构造函数 —> 派生类的构造函数
4.关于符号重载的若干问题
- “=”重载,”=”只能重载为非静态成员函数
- 支持友元函数重载的必要性
class qhy;
qhy a,b;
a = a + b;//(1)
a = b + 1;//(2)
a = 1 + b;//(3)
- 上面 “+” 重载,假设是成员函数的重载,
- 对于(1)来说,只要有参数为qhy的重载,就合法
- 对于(2)来说,只要qhy有参数含int的构造函数,或者是参数为int的”+”重载,就合法
- 对于(3)来说,无论qhy有什么东西,都是非法的,因为 1 + b调用的是 int类型(1的类型) 对于”+”的重载,显然系统自带的类型,没有参数是qhy类型的”+”重载
- 为了解决上面(3)的情况,就需要使用友元函数重载 “+” 号,这样,只要”+”一边有qhy类型,就一定调用qhy里面”+”的友元重载
- 一些习惯
- 运算符操作需要修改对象,用成员函数重载
- 运算符操作数希望有隐式转换,使用友元重载
- 不能用友元重载的符号
- = () [] ->
- 必须友元
- >> <<
5.关于类中的静态成员
- 只有静态常量整型数据成员才可以在类中初始化
class qhy{
static int a = 0; // 非法!
static const int b = 0; //合法!
};
- 非常量静态成员初始化和普通变量的初始化一样,并且在类外初始化
6.类中的非静态常量,要在构造函数中初始化,并且只能使用初始列表初始化
- 普通成员
class qhy{
public:
const int a;
qhy(int _a):a(_a){}//合法!!
qhy(int _a){a = _a;}//非法
};
- 数组成员
class qhy{
public:
const int a[2];//这个没办法初始化,还是使用静态成员数组吧
static const int b[2] = {1,2};//非法!!,正确姿势请看c
static const int c[2] ;
};
const int qhy::c[2] = {1,2};
7.关于类型转换
- 可以通过构造函数实现类型的转换,就比如上面的”+”重载,假设重载参数类型只有qhy,但是调用的时候,其中一个参数不是qhy,假设是int,这个时候会看构造函数是否有int的重载版本,有就调用它,把int转换成qhy
- 类型转换函数
ClassX::operator Type(){
//do something
return Type_Value;
}
- 例子
class qhy{
public:
int a;
operator int(){return a;}
};
int main(){
qhy a;a.a = 10;
cout<<int(a);
}
8.类中重名成员与函数
- 重名成员
class Base{
public:
int a;
Base():a(0){}
};
class qhy : public Base{
public:
int a;
qhy():a(1){}
};
int main(){
qhy a;
cout<<a.a<<" "<<a.Base::a<<"\n";
//1 0
}
- 重名函数
- 用法和上面类似
9.多重继承:一个类可以继承多个类
10.虚继承:若多个基类来个同一个基类,希望只继承一次,就是用虚继承
- 非虚继承,调用了两次B的构造函数
- 虚继承,调用了一次B的构造函数
- 声明,继承前面加上virtual
class qhy :virtual public Base{};
- 虚继承的最高级的基类,就是上面的B,在D的构造函数中提供参数,不受B1,B2构造函数的影响
- 声明,继承前面加上virtual
11.基类的初始化
- 在构造函数初始化列表提供初始化参数,变量名变成基类镜子
class qhy :public Base{
qhy(int a):Base(a){};
};
- 继承基类的顺序决定了构造的顺序
class qhy :public Base1,public Base2{};//先调用Base1的构造函数,在调用Base2的构造函数
12.使用指针引用派生类
- 可以正常访问基类成员
- 必须强制转换成派生类指针后,才能访问派生类的成员
13.使用派生类指针引用基类对象
- 必须强制转换成基类指针之后,才能访问到基类对象
小结:根据指针类型调用指针类型对应的函数,强制转换相当于加上了作用域符::
class Base{
public:
int a,b;
Base():a(0),b(1){}
};
class qhy:public Base{
public:
int b,c;
qhy():b(2),c(3){}
};
int main(){
qhy *pq;Base *pb;
qhy q;Base b;
cout<<b.a<<" "<<b.b<<" "<<q.b<<" "<<q.c<<"\n";//0 1 2 3
pb = &q;//使用基类指针引用派生类
cout<<pb->a<<" "<<pb->b<<"\n";//0 1
//cout<<pb->c<<"\n";//错误,在Base中c不存在
cout<<((qhy*)pb)->a<<" "<<((qhy*)pb)->b<<" "<<((qhy*)pb)->c<<"\n";//0 2 3,b访问到的是qhy的b
//使用派生指针引用基类
//pq = &b;//错误,类型不对
pq = (qhy*)&b;
cout<<pq->b<<pq->c;//乱码,因为b,c不存在
return 0;
}
14.虚函数
- 上面,如果想用基类指针调用派生类对应的函数,每次都要强制转换一下,好麻烦,解决办法:使用虚函数
- 即声明成虚函数之后,不再受到指针类型的影响,直接调用本身的函数
- 虚函数的作用:使用基类指针引用派生类对象时,直接调用派生类对象对应版本的函数
- 声明方法,在函数声明前面加上 virtual
-
纯虚函数:只有声明没有定义的函数,(需要在派生类中定义,否则不能实例化)
virtual int func() = 0;
- 抽象类:含有纯虚函数的类
-
虚析构函数:如果是动态创建的对象,并且使用基类指针来引用的话,delete的时候,调用的是基类的析构函数,如果希望调用派生类对应的析构函数,就把析构函数声明为虚函数
15.public,private,protected属性
- 类成员的属性:
- private:成员只能在类内部和友元内访问,子类和外部都不能访问
- protected:类内部,友元,子类都可访问,类外部不能访问
- public:谁都可以乱搞
- 类的继承方式
- | 基类成员属性 | public | protected | private |
---|---|---|---|---|
继承类型 | ↓继承之后的属性,属性性质→ | 所有都可见 | 类内部,友元,子类可见 | 类内部,友元可见,子类不可见 |
public | 完全不变 | public | protected | private |
protected | public变成protectd,其他保持 | protected | protected | private |
private | 全部变成private | private | private | private |
时间: 2024-11-13 12:30:21