c++ primer 学习笔记21类的定义和声明、隐含的this指针、类作用域
来源:互联网 发布:知乎 欧美系护肤品 编辑:程序博客网 时间:2024/05/16 07:08
类的定义和声明
在 public 部分定义的成员可被使用该类型的所有代码访问;在 private 部分定义的成员可被其他类成员访问。
构造函数
创建一个类类型的对象时,编译器会自动使用一个构造函数来初始化该对象。构造函数是一个特殊的、与类同名的成员函数,用于给每个数据成员设置适当的初始值。
Sales_item(): units_sold(0), revenue(0.0) { }
成员函数
在类内部,声明成员函数是必需的,而定义成员函数则是可选的。在类内部定义的函数默认为 inline(第 7.6 节)。
在类外部定义的成员函数必须指明它们是在类的作用域中。
将关键字 const 加在形参表之后,就可以将成员函数声明为常量:
double avg_price() const;
const 成员不能改变其所操作的对象的数据成员。const 必须同时出现在声明和定义中,若只出现在其中一处,就会出现一个编译时错误。
访问标号实施抽象和封装
在 C++ 中,使用访问标号(第 2.8 节)来定义类的抽象接口和实施封装。
一个类可以没有访问标号,也可以包含多个访问标号:
• 程序的所有部分都可以访问带有 public 标号的成员。类型的数据抽象视图由其 public 成员定义。
• 使用类的代码不可以访问带有 private 标号的成员。private 封装了类型的实现细节。
一个访问标号可以出现的次数通常是没有限制的。每个访问标号指定了随后的成员定义的访问级别。这个指定的访问级别持续有效,直到遇到下一个访问标号或看到类定义体的右花括号为止。
可以在任意的访问标号出现之前定义类成员。在类的左花括号之后、第一个访问标号之前定义成员的访问级别,其值依赖于类是如何定义的。如果类是用 struct 关键字定义的,则在第一个访问标号之前的成员是公有的;如果类是用class 关键字是定义的,则这些成员是私有的。
显式指定 inline 成员函数
在类内部定义的成员函数,将自动作为inline 处理。也就是说,当它们被调用时,编译器将试图在同一行内扩展该函数。也可以显式地将成员函数声明为 inline:
class Screen { public: typedef std::string::size_type index; // implicitly inline when defined inside the class declaration char get() const { return contents[cursor]; } // explicitly declared as inline; will be defined outside the class declaration inline char get(index ht, index wd) const; // inline not specified in class declaration, but can be defined inline later index get_cursor() const; // ... }; // inline declared in the class declaration; no need to repeat on the definition char Screen::get(index r, index c) const { index row = r * width; // compute the row location return contents[row + c]; // offset by c to fetch specified character } // not declared as inline in the class declaration, but ok to make inline in definition inline Screen::index Screen::get_cursor() const { return cursor; }类声明与类定义
将类定义在头文件中,可以保证在每个使用类的文件中以同样的方式定义类。使用头文件保护符(header guard)(第 2.9.2 节),来保证即使头文件在同一文件中被包含多次,类定义也只出现一次。
类对象
定义一个类时,也就是定义了一个类型。一旦定义了类,就可以定义该类型的对象。定义对象时,将为其分配存储空间,但(一般而言)定义类型时不进行存储分配:
class Sales_item {
public:
// operations on Sales_item objects
private:
std::string isbn;
unsigned units_sold;
double revenue;
};
定义了一个新的类型,但没有进行存储分配。当我们定义一个对象
Sales_item item;
时,编译器分配了足以容纳一个 Sales_item 对象的存储空间。
为什么类的定义以分号结束
我们在第 2.8 节中指出,类的定义分号结束。分号是必需的,因为在类定义之后可以接一个对象定义列表。定义必须以分号结束:
class Sales_item { /* ... */ };
class Sales_item { /* ... */ } accum, trans;
通常,将对象定义成类定义的一部分是个坏主意。这样做,会使所发生的操作难以理解。对读者而言,将两个不同的实体(类和变量)组合在一个语句中,也会令人迷惑不解。
隐含的 this 指针
成员函数具有一个附加的隐含形参,即指向该类对象的一个指针。这个隐含形参命名为 this,与调用成员函数的对象绑定在一起。成员函数不能定义 this 形参,而是由编译器隐含地定义。成员函数的函数体可以显式使用 this 指针,但不是必须这么做。如果对类成员的引用没有限定,编译器会将这种引用处理成通过 this 指针的引用。
何时使用 this 指针
尽管在成员函数内部显式引用 this 通常是不必要的,但有一种情况下必须这样做:当我们需要将一个对象作为整体引用而不是引用对象的一个成员时。最常见的情况是在这样的函数中使用 this:该函数返回对调用该函数的对象的引用。
基于 const 的重载
一个是 const,另一个不是 const。基于成员函数是否为 const,可以重载一个成员函数;
可变数据成员
有时(但不是很经常),我们希望类的数据成员(甚至在 const 成员函数内)可以修改。这可以通过将它们声明为 mutable 来实现。可变数据成员(mutable data member)永远都不能为 const,甚至当它是const 对象的成员时也如此。因此,const 成员函数可以改变 mutable 成员。
要将数据成员声明为可变的,必须将关键字 mutable 放在成员声明之前:
class Screen {
public:
// interface member functions
private:
mutable size_t access_ctr; // may change in a const members
// other data members as before
};
我们给 Screen 添加了一个新的可变数据成员 access_ctr。使用 access_ctr 来跟踪调用 Screen 成员函数的频繁程度:
void Screen::do_display(std::ostream& os) const
{
++access_ctr; // keep count of calls to any member function
os << contents;
}
使用类的成员
在类作用域之外,成员只能通过对象或指针分别使用成员访问操作符 . 或 -> 来访问。这些操作符左边的操作数分别是一个类对象或指向类对象的指针。
跟在操作符后面的成员名字必须在相关联的类的作用域中声明:
Class obj; // Class is some class type
Class *ptr = &obj;
// member is a data member of that class
ptr->member; // fetches member from the object to which ptr points
obj.member; // fetches member from the object named obj
// memfcn is a function member of that class
ptr->memfcn(); // runs memfcn on the object to which ptr points
obj.memfcn(); // runs memfcn on the object named obj
一些成员使用成员访问操作符来访问,另一些直接通过类使用作用域操作符(::)来访问。一般的数据或函数成员必须通过对象来访问。定义类型的成员,如 Screen::index,使用作用域操作符来访问。
作用域与成员定义
尽管成员是在类的定义体之外定义的,但成员定义就好像它们是在类的作用域中一样。回忆一下,出现在类的定义体之外的成员定义必须指明成员出现在哪个类中:
double Sales_item::avg_price() const
{
if (units_sold)
return revenue/units_sold;
else
return 0;
}
形参表和函数体处于类作用域中
在定义于类外部的成员函数中,形参表和成员函数体都出现在成员名之后。这些都是在类作用域中定义,所以可以不用限定而引用其他成员。例如,类 Screen 中 get 的二形参版本的定义:
char Screen::get(index r, index c) const
{
index row = r * width; // compute the row location
return contents[row + c]; // offset by c to fetch specified
character
}
函数返回类型不一定在类作用域中
与形参类型相比,返回类型出现在成员名字前面。如果函数在类定义体之外定义,则用于返回类型的名字在类作用域之外。如果返回类型使用由类定义的类型,则必须使用完全限定名。例如,考虑 get_cursor 函数:
class Screen {
public:
typedef std::string::size_type index;
index get_cursor() const;
};
inline Screen::index Screen::get_cursor() const
{
return cursor;
}
通常,名字必须在使用之前进行定义。而且,一旦一个名字被用作类型名,该名字就不能被重复定义:
typedef double Money;
class Account {
public:
Money balance() { return bal; } // uses global definition of
Money
private:
// error: cannot change meaning of Money
typedef long double Money;
Money bal;
// ...
};
类成员定义中的名字查找
按以下方式确定在成员函数的函数体中用到的名字。
1. 首先检查成员函数局部作用域中的声明。
2. 如果在成员函数中找不到该名字的声明,则检查对所有类成员的声明。
3. 如果在类中找不到该名字的声明,则检查在此成员函数定义之前的作用域中出现的声明。
下面的函数使用了相同的名字来表示形参和成员,这是通常应该避免的。
// Note: This code is for illustration purposes only and reflects
bad practice
// It is a bad idea to use the same name for a parameter and a member
int height;
class Screen {
public:
void dummy_fcn(index height) {
cursor = width * height; // which height? The parameter
}
private:
index cursor;
index height, width;
};
查找 dummy_fcn 的定义中使用的名字 height 的声明时,编译器首先在该函数的局部作用域中查找。函数的局部作用域中声明了一个函数形参。dummy_fcn 的函数体中使用的名字 height 指的就是这个形参声明。
在本例中,height 形参屏蔽名为 height 的成员。
尽管类的成员被屏蔽了,但仍然可以通过用类名来限定成员名或显式使用 this 指针来使用它。
函数作用域之后,在类作用域中查找
如果想要使用 height 成员,更好的方式也许是为形参取一个不同的名字:
// good practice: Don't use member name for a parameter or other local
variable
void dummy_fcn(index ht) {
cursor = width * height; // member height
}
类作用域之后,在外围作用域中查找
如果编译器不能在函数或类作用域中找到,就在外围作用域中查找。在本例子中,出现在 Screen 定义之前的全局作用域中声明了一个名为 height 的全局声明。然而,该对象被屏蔽了。
尽管全局对象被屏蔽了,但通过用全局作用域确定操作符来限定名字,仍然可以使用它。
// bad practice: Don't hide names that are needed from surrounding scopes
void dummy_fcn(index height)
{
cursor = width * ::height;// which height? The global one
}
- c++ primer 学习笔记21类的定义和声明、隐含的this指针、类作用域
- C++Primer学习-类(隐含的this指针)
- c++ primer 学习笔记:类之隐含的this 指针
- c++primer 3/7--类作用域,隐含的 this 指针
- 第12章 类 (part1) 类的定义和声明 & 类隐含的 this 指针
- C++Primer学习-类(类的定义和声明)
- c++ primer 学习笔记:类之类的声明和定义
- 【C++】隐含的this指针
- C++ Primer 学习笔记_47_类与数据抽象 --类的定义和声明
- 【C++】【学习笔记】【016】this指针和类的继承
- 隐含的 this 指针
- 隐含的this指针
- 隐含的this指针
- C++ Primer 学习笔记_48_类与数据抽象 -类的定义跟声明【下】
- C++ Primer学习笔记 之 变量的声明与定义
- 类中函数的一个隐含参数--this指针
- C++隐含的this指针
- 12.2隐含的this指针
- unity3d问题集 对Vector3.Lerp 插值的理解 - 转自蛮牛
- 父div高度不能自适应子div高度
- 7z源码分析
- 笔记:基于标签的推荐系统、基于图的推荐算法、PersonalRank
- Error running app: Instant Run requires 'Tools | Android | Enable ADB integration' to be enabled.解决
- c++ primer 学习笔记21类的定义和声明、隐含的this指针、类作用域
- 并查集例题【hdu1232畅通工程,九度1444,hdu5631枚举删除边】
- 修改MFC程序图标
- 设计模式读书笔记之代理模式(Proxy)
- 使用Symantec代码签名证书对代码进行签名的 5 个理由
- Magento SQLSTATE[08S01]: Communication link failure: 1153 Got a packet bigger than 'max_allowed_pack
- 设计模式读书笔记之原型模式(Prototype)
- 先验概率、后验概率、最大似然估计(MLE)
- xUtils简介及其使用方法