C7: Class
7.1 Define a class
介绍如何定义一个类以及类的成员。类的成员包括数据成员和成员函数,数据成员表示类的状态,而成员函数则表示类的行为。在定义一个类时,需要确定类的数据成员和成员函数的访问权限
```
class A {
public: // 习惯给public加一个空格
int a_; // 给成员变量一个下划线
// constructor
explicit A (int value) : a_(value) {}
// const A* const this (top level const, the object cannot be modified)
void func() {
cout << this->a_ << endl;
}
};
int main(){
A a(10);
A a1 = A(10);
// A a2 = 10;
// if no explicit exist: cast to A a2 = A(10)
// if explicit exists, we have to write A a2 = A(10)
A a2 = A(10);
}
```
7.2. Access Control and Encapsulation
C++ 提供了三种访问控制方式:public、protected 和 private。public 成员可以被类的外部访问,protected 成员可以被类的子类和类内部访问,private 成员只能被类的内部访问。访问控制可以帮助控制类的接口,提高代码的安全性和可维护性
// Acess control:
// public
// private
// scope , range
// if I define a friend class/func, I can access your private element directly。但是破坏了封装原则
```
// declaration 声明有这样一个函数
void func1(int, bool);
class Parent_class {
friend void func1(int, bool); // 定义一个友元函数
private:
int a_;
bool b_;
Parent_class(int a, bool b) : a_(a), b_(b) {}
void func() {
cout << "in parent class";
}
};
// define 定义具体的函数实施
void func1(int a, bool b) {
Parent_class parent_class(1, false);
parent_class.func();
cout << parent_class.a_ << endl;
}
```
7.3. Additional Class Features
构造函数可以有多种重载形式,可以在创建对象时进行初始化,而析构函数在对象销毁时自动调用
可变数据成员(mutable)是指可以在 const 成员函数中被修改的数据成员
静态类成员(static)是指属于类本身而不是类的对象的数据成员或函数成员。静态成员变量只有一个副本,所有类的对象共享该变量。静态成员函数则没有 this 指针,可以直接访问静态成员变量
A class must be defined—not just declared—before we can write code that creates objects of that type. Otherwise, the compiler does not know how much storage such objects need.
class ListNode {
private:
ListNode* next; // recursive structure pointer, size is a pointer size!
ListNode* prev;
}
```
class func {
public:
func(int a) : a_(a){}
// 加const之前,this指针是 func* const this
// 但加之后, this 指针是 const func* const this, 不能修改值
void display() const {
cout << this->a_ << endl;
this->a_ = 50; // error, 但是我们可以将a_变成mutable,这样就可以修改a_
cout << "we use mutable a_:" << this->a_ << endl;
}
private:
mutable int a_; // mutable 破坏了const的约定
};
int main(){
func func1(10);
func1.display();
system("pause");
return 0;
}
10
we use mutable a_:50
Press any key to continue . . .
```
```
class func {
public:
func(int a) : a_(a){}
func move(int a) {
this->a_ = a;
cout << "move:" << this->a_ << endl;
return *this; // return copy of the class
}
func& set(int a) {
this->a_ = a;
cout << "set:" << this->a_ << endl;
return *this; // return an object
}
int a_;
};
int main(){
func func1(10);
// why this happened!
// func temp_func1 = func1.move(20);
// then temp.func1.set(30);
// so we cannot set the original a_ to 30, instead we set a temp a_ to 30!
// modify: (simply return a func&, the address!)
// func& move(int a) {
// this->a_ = a;
// cout << "move:" << this->a_ << endl;
// return *this; // return copy of the class
// }
func1.move(20).set(30);
cout << "a_:" << func1.a_ << endl;
system("pause");
return 0;
}
move:20
set:30
a_:20
```
7.3.2 常函数和非常函数:
在 C++ 中,可以通过在成员函数的声明中加上 const 关键字来定义常函数和非常函数。常函数指的是该成员函数不会修改对象的数据成员,而非常函数则指的是该成员函数可以修改对象的数据成员。
常函数的声明如下:
void func() const;
在函数名后面加上 const 关键字即可定义常函数。常函数不能修改对象的数据成员,可以调用对象的其他常函数,但不能调用非常函数。
非常函数的声明如下:
void func();
非常函数可以修改对象的数据成员,也可以调用对象的常函数和非常函数。需要注意的是,如果对象是 const 类型,则不能调用非常函数
在实现成员函数时,需要根据函数是否被声明为常函数来添加 const 关键字,以确保常函数不会修改对象的数据成员。例如:
class MyClass {
public:
int getValue() const {
// 常函数不能修改数据成员
return m_value;
}
void setValue(int value) {
// 非常函数可以修改数据成员
m_value = value;
}
private:
int m_value;
};
7.4 Class scope
7.5
- assign and initialization:
Constructor Initializer List
class A {
public:
int a_;
int b_;
int c_;
A(int a, int b, int c) : a_(a), b_(b), c_(c) {} // initialization
// before assign there is a initialization
A(int a, int b, int c) : a_(0), bool(false), int(0) {
this->a_ = a; // assign
this->b_ = b;
this->c_ = c;
}
// initialization happened before assign!
A(int a, int b, int c) {
this->a_ = a; // assign
this->b_ = b;
this->c_ = c;
}
};
We must use the constructor initializer list to provide values for members that
are const, reference, or of a class type that does not have a default
constructor
class A {
private:
int d_;
public:
int a_;
const int b_;
int& c_;
A(int d, int a, int b, int& c) : b_(b), c_(c) {
this->d_ = d;
this->a_ = a;
}
// const and reference must be initialized! If we assign them within the block, it will be an error!
};
int main(int argc, char** argv){
int c = 40;
A a1(10,20,30,c);
cout << a1.a_ << " " <<a1.b_ << " "<< a1.c_ << endl; // 20 30 40
return 0;
}
7.5.2. Delegating Constructors
-
类里面的成员变量一定要有一个默认构造
In practice, it is almost always right to provide a default constructor if other
constructors are being defined -
千万不要隐式转化 explicit func(int a) : a_(a), b_(b), c_© {}
func(int a, int b, int c) : a_(a), b_(b), c_(c) {
cout << "three args";
}
func(int a, int b) :func(a,b,10) {
cout << "two args";
}
func(int a): func(a,10,20) {
cout << "one arg";
}
void callfunc(func func1) {
cout << "123";
}
int main() {
func func1(10,20);
// three args
// two args
// initialization prioir to assign
callfunc(20);
// one arg
// 123
// that's because func(int a) is implicit it can convert callfunc(20) to callfunc(func(20))
// to solve it, we can add explicite before fun(int a);
.. explicit func(int a) : func(a,10,20)
}
7.6. static Class Members
this 指针是指向当前对象的指针,可以用于访问对象的成员和调用对象的方法。this 指针是隐式的,在成员函数内部使用,无需显式地定义
static 可以声明在类内,但是必须在内外定义!
class Person {
public:
static int count;
};
int Person::count = 0;