✏️

[C++ Primer] OOP

Tags

this

指向当前对象的指针,顶层const
底层一点来看,调用某类的非静态成员函数时,编译器会将这个对象的地址传递给类成员函数,以此来访问到调用该函数的对象
(这一点可以对照lua table的方法调用)
Sales_data total; total.isbn(); // 等价pseudocode: Sales_data::isbn(&total)
 

const成员函数

在成员函数参数表之后标记的const表示,这个成员对应的this指针是底层const的,即不能去修改这个对象的字段
通过这种机制就实现了const成员函数:不能对对象自己产生副作用的成员函数
 

构造函数

默认构造函数:无参的构造函数,对所有字段都做默认初始化
若没有显式定义构造函数则编译器会自动创建
编译器自动创建的构造函数:合成的默认构造函数(synthesized default constructor)
  • 规则:若存在类内初始值则用它来初始化成员,否则默认初始化
  • C++11后可以用 TestClass() = default; 来显式定义
 
委托构造函数:用类中其他构造函数来执行初始化过程
class Sales_data { public: // 非委托构造函数使用对应的实参初始化成员 Sales_data(std::string s, unsigned cnt, double price): bookNo (s), units_sold(cnt), revenue (cnt*price) { } // 其余构造函数全都委托给另一个构造函数 Sales_data(): Sales_data("", 0, 0) {} Sales_data(std:: string s): Sales_data (s, 0,0) {} Sales data(std::istream &is): Sales data() { { read(is, *this); } } };
 
转换构造函数:只接受一个实参的构造函数,这种构造函数实际上定义了从其他类型隐式转换到当前类的方法
class A { public: A (int i) {}; }; void getA(A a); getA(1); // valid
但是注意这种隐式转换只能转一步,即不能连着隐式转两次及以上,中间必须显式地转
如果希望抑制隐式转换则可以在函数名前添加explicit
 
拷贝、移动构造函数:见
✏️
Left / Right Value Reference
中的 拷贝和移动构造函数 一节
 

类成员函数的内联规则

不显式加inline的时候,编译器默认将在类内定义的函数作为内联函数候选,类外定义类内声明的不作为
 
 

class V.S. struct

在C++里,二者实则区别不大,底层机制几乎完全一样(内存布局、虚函数、名称修饰(Name Mangling)规则等)
diff:
class
struct
成员的默认访问权限
private
public
继承时的默认可见性
private
public
 

友元

通过使其他类或者函数成为友元,类可以允许其他类或者函数访问它的非公有成员
在类中用friend声明某个类、函数或者类成员函数
// declaration class TestFriendClass; class TestClass; void outerUpdateX(TestClass& tc, int _x); // definition class TestClass { friend void outerUpdateX(TestClass& tc, int _x); // 如果TestFriendClass没有前向声明,可以直接这么写: // friend class TestFriendClass; friend TestFriendClass; public: TestClass () = default; private: int x; }; class TestFriendClass { public: void updateX(TestClass& tc, int _x) { tc.x = _x; } void clearX() { tc.x = 0; } }; void outerUpdateX(TestClass& tc, int _x) { tc.x = _x; }
 
如果只想设置某个成员函数为友元,则需要遵循下列严格规则
// 1. TestClass的声明 class TestClass; // 2. TestFriendClass的定义,此时不能定义这几个函数,因为TestClass还未声明 class TestFriendClass { public: void updateX(TestClass& tc, int _x); void clearX(TestClass& tc); }; // 3. TestClass的定义 class TestClass { friend void outerUpdateX(TestClass& tc, int _x); friend void TestFriendClass::updateX(TestClass&, int); friend void TestFriendClass::clearX(TestClass&); public: TestClass () = default; private: int x; }; // 4. TestFriendClass的成员函数定义 void TestFriendClass::updateX(TestClass &tc, int _x) { tc.x = _x; } void TestFriendClass::clearX(TestClass& tc) { tc.x = 0; }
 

可变数据成员

mutable关键字修饰一个类成员,表示这个成员无论所属对象的const-ness都能被修改
class TestClass { public: void Update(int) const; private: mutable int x; }; void TestClass::Update(int _x) const { x = _x; }
 
友元声明和作用域:类和非成员函数的声明或定义可以在友元的声明之后
 

基于const的重载

对于成员函数重载,实际上不只通过参数表,还可以通过成员函数的const-ness来区分
此时会由对象本身的const-ness来决定用哪个
class Screen { public: // ...existing code... Screen& display(std::ostream os) { do_display(os); return *this; } const Screen& display(std::ostream os) const { do_display(os); return *this; } private: string contents; void do_display(std::ostream& os) const { os << contents; } } // call Screen myScreen(5, 3); const Screen blank(5, 3); myScreen.display(cout); // 非常量版本 blank.display(cout); // 常量版本
 
 

构造函数初始化

class ConstRef { public: ConstRef(int ii); private: int i; const int ci; int &ri; }; // 只能用这种方式在构造函数里初始化引用和const变量 ConstRef::ConstRef(int ii) : i(ii), ci(ii), &ri(ii) {}
 
 

聚合类

  • 全员public
  • 没有构造函数
  • 没有类内初始值
  • 没有基类和虚方法
可以用一个遵守声明顺序的初始值列表初始化聚合类的数据成员
struct Data { int ival; string s; }; // 顺序需要一致 Data vall = {0, "Anna"};
 

字面值常量类