非静态成员也有相互区别于其他成员的特点,且不同的成员在内存空间的存储位置也不同。而因为非静态成员方法具有对象间共享的特点,所以当它在代码实现时需要用this指针区分不同对象的非静态成员属性。而当采用关键字const修饰非静态成员方法时,实际上是在修饰this指针。
一、非静态成员和成员存储模型
静态成员的特点已经在前文介绍过,而其存储位置均不在类和对象的内存空间。
非静态成员中,非静态成员属性属于由类创建的对象,所以只可以通过对象访问。其存储位置在类和对象的内存空间。
非静态成员方法属于由类创建的对象,所以只可以通过对象访问。由一个类创建的所有对象共享同一份非静态成员方法。其存储位置不在类和对象的内存空间。
class A
{
};
class B
{
public:
int B1;
};
class C
{
public:
//非静态成员属性,存放在类和对象的内存空间
int C1;
//静态成员属性不存放在类和对象的内存空间
static int C2;
//非静态成员方法不存放在类和对象的内存空间
void C3()
{
}
//静态成员方法不存放在类和对象的内存空间
static void C4()
{
}
};
int main()
{
A a;
B b;
C c;
cout << "空类占用的空间为:" << sizeof(A) << endl;
cout << "空对象占用的空间为:" << sizeof(a) << endl << endl;
cout << "仅包含非静态成员属性的类占用的空间为:" << sizeof(B) << endl;
cout << "仅包含非静态成员属性的对象占用的空间为:" << sizeof(b) << endl << endl;
cout << "包含非静态成员属性、静态成员属性、非静态成员方法和静态成员方法的类占用的空间为:" << sizeof(C) << endl;
cout << "包含非静态成员属性、静态成员属性、非静态成员方法和静态成员方法的对象占用的空间为:" << sizeof(c) << endl << endl;
return 0;
}
二、this指针
1.this指针的概念和本质
由对象的成员存储模型可知,由一个类创建的所有对象共享同一份非静态成员方法。如果这段代码中需要使用具体对象或由具体对象创建的非静态成员属性,而调用非静态成员方法时运行的却是同一段代码,就可能会导致代码无法区分是哪个对象在调用自己。为解决该问题C++引入了特殊的对象指针,即this指针。this指针仅能在非静态成员方法中使用,静态成员方法中不能使用。
this指针隐含于每一个非静态成员方法的参数列表内,不需要定义,直接使用即可。其本质为一个指针常量,指向被调用的非静态成员方法所属的对象,且指向不可修改。
class Person
{
public:
//此处在程序执行时的实际代码为
//Person(Person* const this, int age)
Person(int age)
{
//本质为指针常量,指向不可修改
//this = NULL;
this->Age = age;
}
//此处在程序执行时的实际代码为
//void SetAge(Person* const this, int age)
void SetAge(int age)
{
this->Age = age;
}
//此处在程序执行时的实际代码为
//void ShowAge(Person* const this)
void ShowAge()
{
cout << "p.Age = " << Age << endl;
}
private:
int Age;
};
void test()
{
//此处在程序执行时的实际代码为
//Person p = Person(&p, 18);
//p.ShowAge(&p);
//p.SetAge(&p, 30);
//p.ShowAge(&p);
Person p(15);
p.ShowAge();
p.SetAge(30);
p.ShowAge();
}
int main()
{
test();
return 0;
}
2.this指针的使用
当非静态成员方法中的形参和非静态成员属性同名时,需要使用this指针来明确哪个是对象创建的非静态成员属性。但是一般情况下不建议使二者同名,代码风格应尽量简洁。
当需要在非静态成员方法中返回对象本身时,可使用return“ *this; ”。
C++中空指针也可以调用非静态成员方法,如果其代码在实现过程中用到了this指针,需要加以判断保证代码的健壮性。
class Person
{
public:
int score;
//如果不使用this指针,最终显示的结果是随机数,因为程序无法区分形参和对象创建的非静态成员属性
/*Person(int score)
{
score = score;
}*/
Person(int score)
{
//非静态成员方法中的形参和非静态成员属性同名时,需要使用this指针来明确哪个是对象创建的非静态成员属性
this->score = score;
}
Person& ScoreAdd(Person p)
{
this->score += p.score;
//需要在非静态成员方法中返回对象本身时,应使用该语句
return *this;
}
void TestShow1()
{
cout << " 1! 5!" << endl;
}
void TestShow2()
{
//需要此判断以保证代码的健壮性,否则当程序运行时会报错
if (this == NULL)
{
return;
}
//此处在程序执行时的实际代码为
//cout <<"p.score = " << this->score << endl;
//即此非静态成员方法在实现过程中用到了this指针
cout <<"p.score = " << score << endl;
}
};
void test()
{
Person p1(60);
Person p2(90);
Person p3(80);
Person p4(50);
Person p5(30);
Person Sum(0);
//实现所有对象的成绩的不断加和
//链式编程思想
Sum.ScoreAdd(p1).ScoreAdd(p2).ScoreAdd(p3).ScoreAdd(p4).ScoreAdd(p5);
cout << "Sum.score = " << Sum.score << endl;
Person* p = NULL;
//空指针,可以调用非静态成员方法
p->TestShow1();
//但是当非静态成员方法在实现过程中用到了this指针时,由于p指向的对象实际上不存在,程序会报错
p->TestShow2();
}
int main()
{
test();
return 0;
}
三、关键字const修饰非静态成员方法和对象
1.常函数
关键字const能够修饰的函数只有类中的非静态成员方法,此时该非静态成员方法被称为常函数。在常函数中不能修改非静态成员属性,若要实现该功能需要用关键字mutable修饰目标非静态成员属性。常函数始终可修改静态成员属性。
2.常对象
关键字const修饰对象时,该对象称为常对象。常对象也不能修改非静态成员属性,所以只能调用常函数,若可以调用非常函数,可能会间接修改没有用关键字mutable修饰的非静态成员属性。
class Person
{
public:
mutable string Nationality;
static string Car;
string Phone;
Person(string nationality,string car,string phone)
{
this->Nationality = nationality;
this->Car = car;
this->Phone = phone;
cout << Nationality << endl << Car << endl << Phone << endl << endl << endl;
}
//this指针的本质为指针常量,指向不可修改,如果想让指针指向的值也不可修改,需要声明常函数
//const后置修饰非静态成员方法时,实际修饰的是其形参列表中的this指针
//此处在程序执行时的实际代码为
//void SetPerson(const Person* const this, string nationality, string car, string phone)
void SetPerson(string nationality, string car, string phone) const
{
//mutable关键字修饰的非静态成员属性则可修改
this->Nationality = nationality;
//静态成员属性可修改
this->Car = car;
//非静态成员属性不可修改
//this->Phone = phone;
}
//非常函数可能会修改不用mutable关键字修饰的非静态成员属性
void Spy()
{
Phone = "iPhone";
}
};
string Person::Car = "红旗";
void test()
{
//常对象只能调用常函数
const Person person("中国", "比亚迪", "华为");
person.SetPerson("USA", "Tesla", "iPhone");
//person.Spy();
//常对象不能修改非静态成员属性的值,但是可以访问
//person.Phone = "iPhone";
cout << person.Nationality << endl << person.Car << endl << person.Phone << endl << endl << endl;
//常对象可以修改静态成员属性和mutable修饰的非静态成员属性
person.Car = "比亚迪";
person.Nationality = "中国";
cout << person.Nationality << endl << person.Car << endl << person.Phone << endl << endl << endl;
}
int main()
{
test();
return 0;
}