一、c++类里的成员,不管是const,还是static成员都不能直接,初始化;如:
class student
{
private:
int sno=2001; //错误
const int sno=2001; //错误
static int sno=2001; //错误,不能直接初始化成员;
static const int sno=2001; //正确,只能给静态常成员直接赋值
};
const int student::sno=88;//正确
静态成员(方法和成员变量)是属于整个类的不属于某个对象,因此静态方法内没有this指针,即静态方法内也就不能直接调用非静态方法,必须通过对象来调用非静态方法。(因为在非静态方法内直接调用非静态方法时,默认使用this指针来调用,而静态方法内没有this指针,因此不能直接调用非静态方法)。在单例里经常看见在静态方法里通过new来调用构造函数,构造函数不是静态的,为什么能调用呢?
这里要明白,1.构造函数本身就不能显示调用(即通过对象或this指针调用),它是在构建对象时,系统调用的。
1)静态成员的初始化,只能在类外初始化
class student
{
public:
static int sno;
);
int student::sno=1000;//正确,只能这样
且构造函数不能为静态(static)函数;
因为static有两个作用,一个是域限制,一个是生存限制;
域限制:被static修饰的函数,只能本文件中使用。
生存限制:如果是静态类方法,则说明在类对象出生前,类的静态函数就存活着。所以构造函数不能为静态。
类外定义函数时,如果加上static,会编译报错,因为作用域限制只能在该cpp中使用,与类本意冲突,限制类自由。
构造函数也不能为virtual修饰,因为每个类中如果存在virtual函数,那么会维护一张虚函数表(会有一个4个字节虚函数表指针),虚函数调用通过虚函数指针来调用的,如果构造函数为virtual,那么对象都没有实例化,内存空间都没有(虚函数指针不存在),怎么找到虚函数表呢?所以构造函数不能为虚函数。
2)静态函数的定义与调用
class student
{
public:
static void fun() //在类中声明和实现;
{
cout<<"abc"<<endl;
}
static int add(int a,int b); //也可以在类中声明,类外实现
);
//类外实现
int student::add(int a,int b)
{
return a+b;
}
int main()
{
//调用静态的fun方法
student::fun();
return 0;
}
注意:在类中用static声明了的方法,在类外实现时,不能写static修饰,会报错;另外,virtual只能在类中声明,和用于虚继承,不能在类外声明,编译报错。
全局函数默认为extern修饰,如果声明时是extern修饰的,实现时,不能用static修饰,编译报错如:声明为int fun();//是extern修饰
实现: static int fun()
{ cout<<"shixian"<<endl;} //会编译报错
当函数声明为static时,实现不用static修饰,该函数也为静态的,限制在该.cpp中如:
static int fun1();
实现如: int fun1()
{ cout<<"as"<<endl; }//fun1为静态函数
3)常成员的初始化,我们熟知常量是其值不能被改变的,一般都是声明时,直接初始化,但在类中不能直接初始化,只能通过构造函数参数列表来进行初始化。如:
class student
{
public:
const int sno;
student(int a):sno(a) //这样初始化常成员
{}
};
4)常方法的声明与定义,常方法里是不能改变所有成员的值,因此,不能调用非常方法(它们有的会改变成员的值);常方法仅仅只是不能成员的值(mutable修饰的除外)
class student
{
private:
int a;
mutable int b;
public:
const int add(int a,int b); //这个并不是常方法,这只是返回值为const int型的普通函数
//常方法的声明
int add(string a,string b) const //在其函数后加const
{
a=10; //这是错的,常方法里是不能改变 所有成员的值(除了用mutable修饰的成员外)
b=10; //对的,用mutable修饰的可以在常方法里改变其值
}
int f1() const; //类中只声明常方法,类外实现
};
int student::f1() const //同样要在后面加上const修饰
{
return 22;
}
二、此外我们来类比下常量指针和指针常量,以及常引用,和常对象 (常量是不能作为左值)
例如:
const char* s; //表示常量指针,说明*s表示一个常量,通过*s其值不能被改变,所以不能同过*s来给s所指向的地址赋值,但是,s是变量,它的值是可以
被改变的,即s的指向是能被赋值的
char* const s; //表示指针常量,说明s是一个常量,通过s其值是不能被改变的,所以s所指的位置是不变的,但是*s是变量,它的值是可变的,可以通过*s给s所指向的地址赋值
const int& a=b; //表示常引用,a是常引用(等价于常量),通过a不能改变其值,所以不用它来给变量b赋值,但变量b是可以赋值的
注意:引用相当于变量的别名(另一个名字),不占另外的内存空间,和该变量同一地址;另外引用在声明的时候必须要初始化,一个引用只能属于一个变量的引用,且后面不能再给该引用引用其它变量;
const student stu; // student是一个类名,stu表示常对象,即是常量,通过stu其值不会被改变(即通过它,对象里的成员值也不会变),所以常对象只能调用常方法;
const student* strstu; //常对象指针,所指向的是一个常对象,通过strstu指针不能改变其指向对象的值,*strstu(即strstu->)和上面等效;只能调用常方法。
注意:临时变量(即无名的变量)的引用必须是常引用。如:
int fun()
{
return 9;
}
int & a = fun();//错误,编译都通不过,fun返回一个临时的值,必须得用const 引用。
const int& a = fun();//正确;
尽可能不要用引用局部变量,虽然,常引用也能引用局部变量,但是局部变量在函数结束后会被释放掉,此时,在使用引用会出现访问非法地址的错误。
非常引用初始化时,必须用左值(可以被赋值的变量,不能是常量(const修饰))进行初始化,如:
int s;
int & a = s;//正确;
int & b= 19; //错误;
常引用初始化时,可以用常量初始化,
const int &c = 10;//正确;临时的无名对象只能用常引用。