1.隐式的类类型转换
在C++中,将一个double作为一个函数的int参数的实参很常见,我们都知道函数会自动将double转换为int,那么在更普适的条件下,我们可以定义自己的类的自动类型转换,把=赋值语句看做一个接收单参数的函数(实际上确实是一个函数),如果我们自己定义某个类的各种类型转换的函数,或者重载=赋值运算符,那么我们就可以进行隐式的类型转换。
在函数调用中,将实参赋值给形参的过程中可以进行一次(最多一次)类型转换,类型转换不具有传递性,若C类可以转换为B类,B类可以转换为A类,则C类不能通过一次转化成A类,但是可以这样:
class A;
class B;
class C;
A a=B(c);/*将C类对象c传给B的构造函数,一次类型转换后生成一个B类对象,将这个对象进行赋值运算传给A类对象a,又进行了一次类型转换*/
若在构造函数前加上一个explicit关键字(explicit=详尽的),那么构造函数将在将实参传递给形参的过程中拒绝类型转换
由于explicit关键字是为了防止预期外的类型转换,而只有一个实参的构造函数才可能会造成隐式类型转换,所以explicit关键字只对接受一个实参的构造函数有效(有的函数有多个形参,但同时有多个默认实参,也可能成为多形参,单实参的函数,这样的函数也可以进行隐式类型转换)
值得注意的是,在返回函数值的时候同样会进行一次类型转换,例如:
class A
{public:
int ma;
const A& giveback_1();
A& giveback_2 ()const;
}
const A& A::giveback()
{
return *this;//正确,将非const对象赋予const属性的引用
}
A& A::giveback_2()const
{
return *this;//非法,const成员函数的*this是const对象,将const对象赋予非const属性的引用是非法的
}
另外,曾经我们说过构造函数是不能有const关键字的,但是,C++中存在一种字面值常量类
字面值常量类满足以下要求:
a.数据成员都是字面值类型constexpr
b.类必须含有一个constexpr构造函数
c.如果有类内初始值,那么该表达式也应该是常量表达式constexpr
d.必须使用默认的析构函数销毁对象
(综上所属,意思就是,关于这个类的一切都必须是constexpr的,保证了类对象定义的时候就已经知道所有的信息了)
2.静态成员
包括静态成员变量和静态成员函数,它们与类相关而不直接与对象相关。
所有类对象共享一个静态成员变量,当某一个对象修改了静态成员变量时,所有的类对应的静态成员变量都被修改了。
静态成员变量的初始化可以在类内也可以在类外,但是在类内的初始化要求静态成员变量有constexpr属性(这个规定很自然,因为如果一个非常量静态成员可以在类内初始化,那么每次新建一个对象的时候,该静态成员都会被重新初始化一次,而静态成员变量应该是类所共有的,这样的操作不符合共享的原则,这肯定是我们不愿意看到的结果)
在类外的初始化按照命名空间的规则来(属于某个类的静态成员实际上和普通的全局静态一个性质,不过是由编译器设定了访问权限)
const成员函数可以改变静态成员变量,因为静态成员变量并不属于任何一个对象,也就没有触及*this的const属性
相对应的,对于静态成员函数,是不包含对象指针*this的,因而也就不存在const静态成员函数。
以上的静态成员,我们既可以通过对象来访问(虽然与对象没关系,但是用对象来说明作用域),也可以直接通过作用域来访问。
关于静态成员可以是不完全类型(即在使用定义之前并没有完成定义,比如我在定义A类的时候,使之包含一个A类的成员,那么我在完成定义A类之前就使用了A类的定义,非法操作),暂时还不知道它的合理性,我在G++上实测的时候也出现了各种问题(一行代码带来了100行错误代码),暂不解释
静态成员的几个常用用法:
a.用作类的标记,比如我想知道生成了多少个该类对象,那么我就用一个静态成员变量记录,在构造函数中将记录+1
b.做内置宏用,constexpr静态成员变量是字面值,可以看做是类内作用域的宏
c.同类对象之间共享数据,在多线程编程里用作标记之类的