目录
一、类数组
《C++ Primer Plus》中提到初始化数组时都是先默认调用构造函数初始化数组元素,然后将其他非默认构造函数创建的临时对象赋值该对应的数组元素,因此要求类必须提供默认构造函数。实际测试发现不存在将临时对象赋值给对应数组元素这一步,没有默认构造函数的类也能正常创建类数组,测试代码如下:
#include <iostream>
class ClassA {
private:
int a = 1;
double b;
public:
ClassA();
ClassA(int a, double b);
void print();
};
ClassA::ClassA() {
a = 1;
b = 0;
std::cout << "execute ClassA()\n";
}
ClassA::ClassA(int a, double b) {
this->a = a;
this->b = b;
std::cout << "execute ClassA(int a,double b)\n";
}
void ClassA::print() {
std::cout << "ClassA,a=" << a << ",b=" << b << std::endl;
}
int main() {
std::cout << "a init:\n";
//自动调用默认构造函数
ClassA a[5];
std::cout << "a:\n";
for (int i = 0; i < 5; i++) {
a[i].print();
}
std::cout << "b init:\n";
//前3个调用有参构造函数,最后2个调用默认构造函数
//不存在先调用默认构造函数初始化数组元素,然后将有参构造函数创建的临时对象赋值给数组元素这一步
ClassA b[5] = { ClassA(1, 2), ClassA { 3, 4 }, ClassA { 5, 6 } };
std::cout << "b:\n";
for (int i = 0; i < 5; i++) {
b[i].print();
}
return 0;
}
上述示例,如果注掉默认构造函数,把b的数组大小改成3,依然可以正常编译执行。
二、类作用域枚举
声明在类定义中的匿名枚举相当于static const常量,枚举常量可在类作用域内访问。
普通的枚举作用域范围按定义的位置可能是块作用域或者文件作用域,如果同一个作用域内存在相同枚举常量的枚举则编译报错枚举常量重复定义。枚举定义时加上class或者struct,可以限制枚举作用域的范围在指定的类下,允许不同的类的枚举常量一直。
#include <iostream>
class ClassA {
private:
//类作用域下的匿名枚举,类定义内都可以使用MONTH,编译器自动替换为12,等价于常规的static const用法
enum {MONTH=12};
// static const int MONTH=12;
int a = 1;
double b[MONTH]={1,2,3,4};
public:
ClassA();
void print();
};
//编译警告 list-initializer for non-class type must not be parenthesized
//ClassA::ClassA():b({1,2,3,4}),a(1) {
ClassA::ClassA(){
// a = 1;
//数组只能在初始化时赋值,不能二次赋值,所以这里编译报错,需要使用声明初始化方式或者列表初始化方式
// b = {1,2,3,4};
}
void ClassA::print() {
double sum=a;
for(int i=0;i<MONTH;i++){
sum+=b[i];
}
std::cout <<"sum="<<sum <<"\n";
}
int main(){
double dou[12]={};
ClassA a;
a.print();
using namespace std;
//A的作用域为整个main方法
enum a{A=10,B,C};
//报错A,B,C重复声明
// enum b{A=1,B,C};
//加上class或者struct以后,作用域被限制在类c中
enum class c{D=20,E,F};
enum struct d{D=30,E,F};
//cout的符号重载没有针对枚举enum c的实现,所以需要显示类型转换成int
cout<<"enum A:"<<A <<",c::D:"<< (int)c::D << ",d::D:"<<(int)d::D <<endl;
//当赋值给int变量或者执行比较时,常规枚举会自动做类型转换
int b=A;
//作用域枚举不能自动转换,需要显示转换
// int c=c::E;
int c=(int)c::E;
//指定作用域枚举底层的整型数据类型,默认是int,但是GNU 不支持,报语法错误
// enum class :short e{D=20,E,F};
return 0;
}
三、运算符重载
函数重载是同名函数支持不同特征标的入参,运算符重载是允许同一个操作符如+支持不同的操作数类型如用户自定义的类类型,两者都是为了提高代码本身的可读性。运算符重载是编译器提供的语法糖,通过运算符执行的操作最终会被编译器转换成对特定的运算符函数的调用。运算符函数跟普通函数的唯一区别是函数名必须是operator+运算符,如operator* 是对操作符*的运算符函数。运算符函数可以是类成员函数,也可以是普通的非类成员函数,只要符合函数名要求即可,运算符函数本身也可重载,编译器会根据操作数类型自动匹配核实的运算符函数。
1、通过类成员函数重载运算符
#include <iostream>
class ClassA{
private:
int a;
public:
ClassA();
ClassA(int i);
//双目运算符
ClassA operator+(ClassA & a);
ClassA operator+(int b);
//自增运算符,前置模式
void operator++();
//后置模式,入参必须是int,只做标记,实际传0,编译器据此区分是后置模式
void operator++(int);
//单目运算符
int operator&();
void print();
};
ClassA::ClassA(){
}
ClassA::ClassA(int i){
a=i;
}
ClassA ClassA::operator +(ClassA & cl){
int sum=this->a+cl.a;
return ClassA(sum);
}
void ClassA::operator ++(){
a++;
}
void ClassA::operator ++(int){
a++;
}
int ClassA::operator &(){
retu