C++中 类
1.this指针
class应该理解为一种类型,象int,char一样,是用户自定义的类型。用这个类型可以来声明一个变量,比如int x, myclass my等等。这样就像变量x具有int类型一样,变量my具有myclass类型。理解了这个,就好解释this了,my里的this 就是指向my的指针。如果还有一个变量myclass mz,mz的this就是指向mz的指针。 这样就很容易理解this 的类型应该是myclass *,而对其的解引用*this就应该是一个myclass类型的变量。
通常在class定义时要用到类型变量自身时,因为这时候还不知道变量名(为了通用也不可能固定实际的变量名),就用this这样的指针来使用变量自身。
- this指针的用处:
一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。
this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。例如,调用date.SetMonth(9) <===> SetMonth(&date, 9),this帮助完成了这一转换 .
在成员函数内部,我们可以直接使用调用该函数的对象的成员,而无需通过成员访问运算符来做到这一点,因为this所指的正是这个对象。任何对类成员的直接访问都被看成this的隐式使用。
this的目的总是指向这个对象,所以this是一个常量指针,我们不允许改变this中保存的地址 - this指针的使用:
一种情况就是,在类的非静态成员函数中返回类对象本身的时候,直接使用 return *this;
另外一种情况是当参数与成员变量名相同时,如this->n = n (不能写成n = n)。 - this指针程序示例:
this指针是存在与类的成员函数中,指向被调用函数所在的类实例的地址。
根据以下程序来说明this指针:
#include<iostream.h>
class Point
{
int x, y;
public:
Point(int a, int b) { x=a; y=b;}
Void MovePoint( int a, int b){ x+=a; y+=b;}
Void print(){ cout<<"x="<<x<<"y="<<y<<endl;}
};
void main( )
{
Point point1( 10,10);
point1.MovePoint(2,2);
point1.print( );
}
当对象point1调用MovePoint(2,2)函数时,即将point1对象的地址传递给了this指针。
MovePoint函数的原型应该是 void MovePoint( Point *this, int a, int b);第一个参数是指向该类对象的一个指针,我们在定义成员函数时没看见是因为这个参数在类中是隐含的。这样point1的地址传递给了this,所以在MovePoint函数中便显式的写成:
void MovePoint(int a, int b) { this->x +=a; this-> y+= b;}
即可以知道,point1调用该函数后,也就是point1的数据成员被调用并更新了值。
即该函数过程可写成 point1.x+= a; point1. y + = b;
转载自:https://blog.youkuaiyun.com/chenyt01/article/details/51316022
2.类中构造函数的初始化列表
2.1一般类中构造函数的初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
初始化列表的成员初始化顺序:C++初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。
构造函数两种初始化情况举例:
class CExample {
public:
int a;
float b;
//构造函数初始化列表
CExample(): a(0),b(8.8) {}
//构造函数内部赋值
CExample() {
a=0;
b=8.8;
}
};
上面的例子中两个构造函数的结果相同。例子中,
CExample(): a(0),b(8.8) {}
:使用初始化列表的构造函数显式的初始化类的成员;CExample() { a=0; b=8.8; }
:未使用初始化列表的构造函数是对类的成员赋值,并没有进行显式的初始化;
初始化和赋值对内置类型的成员无大的区别。但对非内置类型成员变量,为了避免两次构造,推荐使用类构造函数初始化列表。
必须用带有初始化列表的构造函数的情况:
- 成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。
- const成员或引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。
上述情况1示例:
class Pet {
private:
string type;
public:
Pet(string type) { this->type = type; }
string getPetType() { return this->type; }
};
class InPet {
private:
Pet ipet;
int count;
public:
InPet(Pet ipet,int count) : ipet(ipet){
//this->ipet = ipet;//此种情况只能使用初始化列表
this->count = count;
}
};
总结:
初始化数据成员与对数据成员赋值的含义是什么?有什么区别?
首先把数据成员按类型分类并分情况说明:
- 内置数据类型,复合类型(指针,引用)
在成员初始化列表和构造函数体内进行,在性能和结果上都是一样的- 用户定义类型(类类型)
结果上相同,但是性能上存在很大的差别。因为类类型的数据成员对象在进入函数体前已经构造完成,也就是说在成员初始化列表处进行构造对象的工作,调用构造函数,在进入函数体之后,进行的是对已经构造好的类对象的赋值,又调用个拷贝赋值操作符才能完成(如果并未提供,则使用编译器提供的默认按成员赋值行为)
转载自:https://www.cnblogs.com/BlueTzar/articles/1223169.html
2.2派生类构造函数的初始化列表
派生类不能继承基类的构造函数,必须自己定义构造函数进行新增数据成员初始化工作,如果想同时初始化基类数据成员,必须调用基类构造函数。
class stu1 {
public:
stu1(int i, string strn, double in) {
ID = i;
name = strn;
income = in;
}
private:
int ID;
string name;
double income;
};
class stu2 :public stu1
{
public:
stu2(int i, const string str, double in, string p) :stu1(i, str, in) {
phoneNo = p;
}
private:
string phoneNo;
};
int main() {
stu2 st(1, "Tom", 1000.02, "12345");
}
构造一个类的对象之前,必须先构造其中的嵌套类对象,若没给嵌套类传参数,则调用嵌套类的默认构造函数,否则调用嵌套类的带参数的构造函数。
转载自:https://www.cnblogs.com/hi3254014978/p/9810170.html
3 具有继承关系的父类与子类之间的类型转换问题
具有继承关系的父类与子类之间的类型转换:
- 从子类到父类大类型转换:向上转型,不需特殊操作
- 从父类到子类大类型转换:向下转型,需要
dynamic_cast<>
操作
析:子类转父类
子类是从父类继承而来,子类中包含父类中所有成员。在转换成父类的过程中,意味着对子类进行了一个切割,只是将子类中的父类部分赋值给了父类对象。
但在子类转换成父类的时候,也需要注意,只能是指针或者引用,不能是对象。
析:父类转子类
对于父类转换成子类,意味着将子类中将有一部分是未知的成员。这就需要dynamic_cast<>
。
示例:
#include <iostream>
using namespace std;
class CPoint
{
public:
virtual void f() { cout << "CPoint::f" << endl; }
};
class CPoint3D : public CPoint
{
public:
virtual void f() { cout << "CPoint3D::f" << endl; }
};
int main()
{
CPoint point, *pPoint;
CPoint3D point3D, *pPoint3D;
//子类向父类类型转换,直接强制转换就可以了,但实际仍然指向子类对象
pPoint = (CPoint*)&point3D;
pPoint->f(); //调用的仍然是子类的函数,这便是虚函数的魅力所在
//父类向子类类型转换,要用dynamic_cast
pPoint3D = dynamic_cast<CPoint3D*>(pPoint);
pPoint->f(); //pPoint实际指向一个CPoint3D对象,故可以正确转换
pPoint3D = dynamic_cast<CPoint3D*>(&point);
cout << (int*)pPoint3D << endl; //&point指向的不是一个CPoint3D对象,返回值为NULL
return 0;
}
结果:
CPoint3D::f
CPoint3D::f
00000000
dynamic_cast<type-id> ( expression )
:
定义:将一个基类对象指针(或引用)cast到继承类指针,dynamic_cast<>
会根据基类指针是否真正指向继承类指针来做相应处理。
作用:该运算符把expression
转换成type-id
类型的对象。type-id
必须是类的指针、类的引用或者void*
;
析:如果type-id
是类指针类型,那么expression也必须是一个指针,如果 type-id 是一个引用,那么expression
也必须是一个引用。
dynamic_cast
运算符可以在执行期决定真正的类型。
- 如果 downcast 是安全的(也就说,如果基类指针或者引用确实指向一个派生类对象)这个运算符会传回适当转型过的指针。
- 如果 downcast 不安全,这个运算符会传回空指针(NULL)(也就是说,基类指针或者引用没有指向一个派生类对象)。