文章目录
一、嵌套类型设计的构造函数实现
什么是构造函数的初始化列表,它有什么功能呢?我们来看这个例子:CData信息是CGoods商品信息的一部分:a part of关系。
//日期类
class CDate
{
public:
CDate(int y, int m, int d)
{
_year = y;
_month = m;
_day = d;
}
void show()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
//商品类
class CGoods
{
public:
CGoods(char *n, int a, double p)
{
strcpy(_name, n);
_amount = a;
_price = p;
}
void show()
{
cout << "name:" << _name << endl;
cout << "amount:" << _amount << endl;
cout << "price:" << _price <<endl;
}
private:
char _name[20];
int _amount;
double _price;
CDate _date;//CDate类定义的一个对象作为CGoods类的成员变量,即成员对象
};
这里会发生错误:
当我们定义了一个对象却没有指定它的构造方式时,它用的就是它的默认构造函数。我们这里并没有指定CDate的构造方式,编译时,因为我们自定义了一个默认构造,此时编译器不会产生默认构造了,编译出错。
那么年,月,日的信息是如何传递给_date的?如何在CGoods代码中指定构造方式?此时就需要构造函数的初始化列表了。
构造函数的初始化列表:可以对当前对象的成员变量指定初始化方式。成员变量的初始化顺序是以他们定义的顺序初始化的,与构造函数初始化列表中出现的先后顺序无关。
方式1:
CGoods(char *n, int a, double p, int y, int m, int d)
:_date(y, m, d)//指定_date对象构造方式,构造函数的初始化列表,先执行 //Cdate _date(y, m, d);
//对当前对象的成员变量指定初始化方式
{
//当前类类型构造函数体,后执行
strcpy(_name, n);
_amount = a;//int _amount; _amount = a;
_price = p;//int _price; _price = p;
}
方式2:
CGoods(char *n, int a, double p, int y, int m, int d)
:_date(y, m, d)
,_amount(a)//int _amount = a;
,_price(p)//double _price = p;
//对当前对象的成员变量指定初始化方式
{
//当前类类型构造函数体,后执行
strcpy(_name, n);
}
我们修改后测试一下:测试成功。
我们再来看一个实例:增强对它的理解。
思考一下,ma,mb分别打印的结果是什么?
class Test
{
public:
Test(int data = 10):mb(data), ma(mb){}
void show()
{
cout << "ma:" << ma << " mb:" << mb << endl;
}
private:
int ma;
int mb;
};
int main()
{
Test t;
t.show();
return 0;
}
打印结果分析: 当我们生成一个对象的时候,对象的成员变量初始化方式若没有在构造函数初始化列表中指定,则使用默认构造,即没有初始化。若在构造函数的初始化列表中初始化了,那么成员变量的初始化顺序是以他们定义的顺序初始化的。因此ma先初始化,此时mb还没有值,即0xcccccccc;mb再初始化即10。
二、this指针和成员方法之间的关系
2.1、普通成员方法与静态成员方法
普通的成员方法特点:编译器会添加一个this形参变量
1.属于类的作用域。
2.调用时使用对象调用(常对象无法调用)。
3.可以任意访问对象的私有成员。(protected暂时不考虑)。
静态成员方法特点:不会生成this形参
1.属于类的作用域。
2.调用时使用类名作用域调用。
3.若一个方法访问的是所有对象共享的信息,我们最好使用静态方法。
4.可以任意访问对象的私有成员,仅限于不依赖对象的成员(仅能调用其它的static成员)。
案例1: 我们先来看一个例子,继续看上面那个商品类,此时我们若定义了多个商品,想记录商品的总数量,于是我们这样定义可以吗?
private:
char _name[20];
int _amount;
double _price;
CDate _date;//CDate类定义的一个对象作为CGoods类的成员变量,即成员对象
int _count;//用来记录商品对象的总数量
};
答案是否定的,因为我们把_count定义成为了一个普通的成员变量,当我们用CGoods定义对象的时候,每一个对象都有自己的一个_count,每一个_count只能描述一个商品对象的信息,不能描述所有商品的信息。因此我们使用静态成员变量,它是所有对象所共享的,类内只是声明,类外实现定义。静态成员变量不属于对象,是属于类级别的。 当我们再次计算对象大小的时候,静态成员变量不纳入对象内存的。每次定义一个新的对象会调用构造函数,对_count进行++,即可统计。
类内:
private:
char _name[20];
int _amount;
double _price;
CDate _date;//CDate类定义的一个对象作为CGoods类的成员变量,即成员对象
static int _count;//静态成员变量,类内只是声明,需要在类外定义,用来记录商品对象的总数量
类外:
//静态成员变量一定要在类外进行定义并且初始化
int CGoods::_count = 0;//.bss段
因此实现代码为:
//日期类
class CDate
{
public:
CDate(int y, int m, int d)
{
_year = y;
_month = m;
_day = d;
}
void show()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
//商品类
class CGoods
{
public:
CGoods(const char *n, int a, double p, int y, int m, int d)
:_date(y, m, d)//指定_date对象构造方式,构造函数的初始化列表,先执行
,_amount(a)
,_price(p)
//对当前对象的成员变量指定初始化方式
{
//当前类类型构造函数体,后执行
strcpy(_name, n);
_count++;//记录所有产生的新对象的数量
}
void show()//打印商品私有信息
{
cout << "name:" << _name << endl;
cout << "amount:" << _amount << endl;
cout << "price:" << _price <<endl;
_date.show();
cout << endl;
}
void showCGoodsConut()//打印商品所有共享信息
{
cout << "所有商品的种类数量是:" << _count << endl;
}
private:
char _name[20];
int _amount;
double _price;
CDate _date;//CDate类定义的一个对象作为CGoods类的成员变量,即成员对象
static int _count;//静态成员变量,类内只是声明,需要在类外定义,用来记录商品对象的总数量
};
//静态成员变量一定要在类外进行定义并且初始化
int CGoods::_count = 0;
int main()
{
CGoods good1("商品1", 100, 35.0, 2019, 5, 12);
good1.show();
CGoods good2("商品2", 100, 35.0, 2019, 5, 12);
good2.show();
CGoods good3("商品3", 100, 35.0, 2019, 5, 12);
good3.show();
CGoods good4("商品4", 100, 35.0, 2019, 5, 12);
good4.show();
//统计所有商品的总数量
good4.showCGoodsConut();
return 0;
}
测试结果:成功统计
虽然上面代码完成了我们的功能,打印的是所有商品的信息。但是为什么要使用good4调用呢,这不符合我们的逻辑。那我们只用类名调用可以吗?
CGoods::showCGoodsConut();
当然不行,它只是一个普通的成员方法,编译器编译时会产生this指针,调用时必须要传入调用对象的地址,但此时没有对象。因此我们想访问所有对象共享的信息,不能将它写为普通方法用对象去调用它,应该使用类名去调用它,我们需将其写为静态成员方法。
//静态成员方法
static void showCGoodsConut()//打印商品所有共享信息
{
cout << "所有商品的种类数量是:" << _count << endl;
}
静态成员方法使用类的作用域调用:不依赖于this指针,不需要接收对象的地址。
CGoods::showCGoodsConut();
2.2、常成员方法
常成员方法: 如果一个方法是只实现读操作而不实现写操作的成员方法,一律实现成const常成员方法。普通对象可以调用,常对象也能调用。
常成员方法特点:
1.常成员方法产生const 类类型 * this。
2.属于类的作用域
3.调用依赖于一个对象,普通对象或者常对象都可以。
4.可以任意访问对象的私有成员,只能读不能写。
案例2: 常对象调用普通方法:常对象无法调用普通成员方法
此时若有一个非卖品对象:good5
const CGoods good5("非卖品商品5", 100, 35.0, 2019, 5, 12);
good5.show();
我们打印一下信息:编译器报错,
分析一下: good5为const CGoods* 类型,而普通方法show生成的是CGoods *this类型,因此会报错。若想接收,形参必须也是const CGoods *this类型。因此,修改如下:将其写为常方法,普通对象能调用,常对象也可以调用。
//常成员方法
//CDate中的show()方法
void show()const
{
cout << _year << "/" << _month << "/" << _day << endl;
}
//CGoods中的show()方法
void show()const//const CGoods *this
{
cout << "name:" << _name << endl;
cout << "amount:" << _amount << endl;
cout << "price:" << _price <<endl;
_date.show();
cout << endl;
}
案例3:常对象可以任意访问对象的私有成员,只能读不能写。
我们尝试对_price进行修改
//常成员方法
void show()const//const CGoods *this
{
cout << "name:" << _name << endl;
cout << "amount:" << _amount << endl;
cout << "price:" << _price <<endl;
_price = 20;
_date.show();
cout << endl;
}
编译器报错:
三、指向类型成员的指针使用实例
3.1普通成员变量使用实例
普通成员变量:我们定义指针指向类的成员变量,前面需要添加类的作用域。而且必须依赖于对象。
实例1:调用普通成员变量
class Test
{
public:
void func()//普通成员方法
{
cout << "call Test::func" << endl;
}
static void static_func()//静态成员方法
{
cout << "Test::static_func" << endl;
}
int ma;//普通成员变量
};
int main()
{
int *p = &Test::ma;//普通指针指向整型成员变量
*p = 20;
return 0;
}
执行结果:编译器报错
&Test::ma类型编译器认为是加了类的作用域,因此我们定义指针时,也需要加上类的作用域,是类作用域里的整型变量。当访问ma时必须给前面加上对象。
①我们在栈上定义一个对象:
int main()
{
Test t1;//栈上
int Test::*p = &Test::ma;//普通指针指向整型成员变量
t1.*p = 20;
cout << t1.*p << endl;
return 0;
}
修改成功:
②我们在堆上定义一个对象:
int main()
{
Test *t2 = new Test();//堆上
int Test::*p = &Test::ma;//普通指针指向整型成员变量
t2->*p = 30;
cout << t2->*p << endl;
delete t2;
return 0;
}
修改成功:
因此,我们定义指针指向类的成员变量,前面需要添加类的作用域。
3.2静态成员变量使用实例
静态成员变量:不依赖对象。
实例2:调用静态成员变量
class Test
{
public:
void func()//普通成员方法
{
cout << "call Test::func" << endl;
}
static void static_func()//静态成员方法
{
cout << "Test::static_func" << endl;
}
int ma;//普通成员变量
static int mb;//静态成员变量
};
int Test::mb;
int main()
{
int *p1 = &Test::mb;
*p1 = 40;
cout << *p1 << endl;
return 0;
}
修改成功:
3.3指向普通成员方法的指针使用实例
实例3:调用普通成员方法
//指向成员方法的指针
void (*pfunc)() = &Test::func();
(*pfunc)();
编译出错:
因此我们必须加上类的作用域与对象来调用:
Test t1;//栈上
Test *t2 = new Test();//堆上
//指向成员方法的指针
void (Test::*pfunc)() = &Test::func;
(t1.*pfunc)();
(t2->*pfunc)();
调用成功。
3.4指向成员方法的指针使用实例
实例4:调用静态成员方法
Test::static_func();
调用成功。