可以在类声明的外部进行成员函数的具体定义
类中成员函数不一定在定义类的时候就需要立即定义其函数体内容,可以在类的外部进行定义,例如: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, '%');
}