【C++之类与对象】成员函数、复制构造函数、前向引用声明、结构体、联合体union、

可以在类声明的外部进行成员函数的具体定义

类中成员函数不一定在定义类的时候就需要立即定义其函数体内容,可以在类的外部进行定义,例如:Rectangle类的成员函数getArea()就在类的定义外部进行定义,需要用上作用域运算符(::),用于标明函数体所属的类

class Rectangle1 {
public:
	Rectangle1(int a, int b) {
		length = a;
		high = b;
	}
	inline int getArea();
private:
	int length;
	int high;
};
inline int Rectangle1::getArea() {
	return length * high;
}

值得注意的是,在类内定义的成员函数,默认为内联函数,若在外部定义函数,也可以使用内联函数(关键字:inline)

复制构造函数(系统有默认,可自定义)

何时会使用到复制构造函数:
1、当用类的一个对象去初始化该类的另一个对象时,系统会自动调用复制构造函数实现复制赋值;
2、若函数的形参为类对象,调用函数时,实参赋值给形参,系统也会自动调用复制构造函数;
3、当函数的返回值为类对象时:
使用方法:例如在定义复数的类中的复制构造函数:


class Complex {
public:
	Complex(int real, int imag) {
		Real = real;
		Imag = imag;
		printf("调用构造函数\n");
	}
	Complex(const Complex& p) {//定义复制构造函数
		Real = p.Real;
		Imag = p.Imag;
		printf("调用复制构造函数\n");
		printf("复制虚数后,实部为:%d,虚部为:%d\n", Real, Imag);
	}
	int getReal() {
		return Real;
	}
	int getImag() {
		return Imag;
	}
private:
	int Real;
	int Imag;
};

注意上面程序的复制构造函数使用的形参为const Complex& p,此为常引用,其中const可以防止传入的类对象被函数修改,而&为引用符号,为传入的对象起一个别名,不使用&引用符号将会报错。如以Complex类举例三种不同的使用到复制构造函数场景:
1、当用类的一个对象去初始化该类的另一个对象时:如下程序,将对象c1赋值给对象c2,将会自动调用复制构造函数

int main() {
	Complex c1(1, 1);
	printf("开始赋值虚数:\n");
	Complex c2 = c1;
}
/*输出:
调用构造函数
开始赋值虚数:
调用复制构造函数
复制虚数后,实部为:1,虚部为:1
*/

2、若函数的形参为类对象,调用函数时,实参赋值给形参

void printComplex(Complex c) {
	printf("打印虚数: %d + %dj\n", c.getReal(), c.getImag());
}
int main() {
	Complex c1(1, 1);
	printComplex(c1);
}
/*输出:
调用构造函数
调用复制构造函数
复制虚数后,实部为:1,虚部为:1
打印虚数: 1 + 1j
*/

这里需要提醒一下,当形参不是Complex c,而是Complex &c时,因为用到引用,所以传入的对象并没有被复制到新的对象中,引用只是为传入的对象起了一个别名,因此不存在调用复制构造函数,代码改为如下,有不同的结果:

void printComplex(Complex &c) {
	printf("打印虚数: %d + %dj\n", c.getReal(), c.getImag());
}
int main() {
	Complex c1(1, 1);
	printComplex(c1);
}
/*输出:
调用构造函数
打印虚数: 1 + 1j
*/

3、当函数的返回值为类对象时

Complex Add(Complex &c1, Complex &c2) {
	Complex c(c1.getReal() + c2.getReal(), c1.getImag() + c2.getImag());
	return c;
}
int main() {
	Complex c1(1, 1);
	Complex c2(1, 2);
	Complex c = Add(c1, c2);
}
前向引用声明

有一种情形:类A和类B互相引用,类A在使用类B前,需要对类B进行前向引用声明
正确如下:

class B;//在类A使用类B之前首先进行前向引用声明
class A {
public:
	void f(B b);
};
class B {
public:
	void f(A a);
};

但是!!注意:尽管使用了前向引用声明,但是在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象,只能使用被声明的符号,而不能涉及类的任何细节。
若如下程序则会报错:C2027 使用未定义类型“B”

class B;
class A {
public:
	void f(B b) {
		printf("%d\b", b.x);//报错:使用未定义类型“B”
	}
	int x = 0;
};
class B {
public:
	void f(A a);
	int x = 0;
};

而正确可以如下:等到类B的完整声明后,在类A的外部定义成员函数,就可以使用类B了

class B;
class A {
public:
	void f(B b);
	int x = 0;
};
class B {
public:
	void f(A a);
	int x = 0;
};

void A::f(B b) {
	printf("%d\b", b.x);
}
结构体

需要注意的是,结构体是一种特殊形式的类。但与类的唯一区别是:结构体中成员的缺省访问属性为public,而在类中是private。

联合体(关键字union)

联合体是一种特殊的类,缺省访问属性为public
作用是:联合体的全部数据成员共享一组内存单元,可以用来节省空间,有的场合不需要让每个数据类型都占用不同的内存。
假设一个情景,一个学生的成绩优劣可以有三种不同的表现形式:分数、是否达标、排名前百分之多少。有时候可以用一组内存单元来分别存取这三个不同的数据,只要不需要同时存储即可,如下程序:得到对应的数据,则可以马上输出,故不需要为每一个不同类型的数据分配不同的内存单元,可以用联合体来节省空间。

union Mark {
	int grade;
	bool pass;
	double percent;
};
int main() {
	Mark mark;
	mark.grade = 90;
	printf("成绩为:%d\n", mark.grade);
	mark.pass = true;
	printf("是否通过: %s\n", mark.pass ? "是" : "否");
	mark.percent = 98.32;
	printf("排名前 %.2f%c\n", mark.percent, '%');
}
/*输出
成绩为:90
是否通过: 是
排名前 98.32%
*/

但是需要注意的是:如上程序,当赋值给pass变量值后,grade的变量值不再是原来的90,不要再访问,否则数据可能是不对的。
使用联合体也可以不用为其命名一个标记名,即可以使用“无名联合体”。
无名联合体:没有标记名,只是声明一个成员项的集合,这些成员项具有相同的内存地址,可以由成员项的名字直接访问。改动上面程序为如下,除了没有标记名,还必须声明为静态的(关键字:static),输出结果同上。

static union {
	int grade;
	bool pass;
	double percent;
};
int main() {
	grade = 90;
	printf("成绩为:%d\n", grade);
	pass = true;
	printf("是否通过: %s\n", pass ? "是" : "否");
	percent = 98.32;
	printf("排名前 %.2f%c\n",percent, '%');
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值