C++11类(1) 基础技巧 Class Basic
来源:互联网 发布:康师傅 统一 知乎 编辑:程序博客网 时间:2024/04/25 09:55
Reference:The C++ Programming Language 4th edition (Bjarne Stroustrup)
1.explicit构造函数
单参数的构造函数会默认作为参数类型到此类类型隐式类型转换,在许多情况下这种隐式的类型转换很有可能导致错误
幸运的是,我们可以用explicit关键字显示声明构造函数(并不必须是单参数)不用作隐式的类型转换
struct X { explicit X(); explicit X(int,int);};X x1 = {}; // error : implicitX x2 = {1,2}; // error : implicitX x3 {}; // OK: explicitX x4 {1,2}; // OK: explicitint f(X);int i1 = f({}); // error : implicitint i2 = f({1,2}); // error : implicitint i3 = f(X{}); // OK: explicitint i4 = f(X{1,2}); // OK: explicit
Good Practice: 默认将单参数的构造函数声明为explicit,除非有强力的需求不这么做。
2. 类内初始化
当存在多个构造函数时, 构造函数的默认参数可能会造成冗余,此时可以对数据成员使用初始化设定项(initializer)
class Date {int d {today.d};int m {today.m};int y {today.y};public:Date(int, int, int); // day, month, yearDate(int, int); // day, month, today’s yearDate(int); // day, today’s month and yearDate(); // default Date: todayDate(const char∗); // date in string representation// ...
3. 类内定义函数
类内定义的函数会被认为是内联函数(inline),通常小型的,使用频率高,几乎不会修改的函数会被定义在类内。
class Date {public: void add_month(int n) { m+=n; } // increment the Date’s m// ...private: int d, m, y;};
4. 可变性(Mutability)
(1) 常成员函数
在函数的声明和定义中都要在参数列表后加const,即const是函数类型的一部分
常对象只能调用常成员函数,非常(non-const)对象可以调用常成员函数和非常成员函数
void Date::add_year(int n); void f(Date& d, const Date& cd){ int i = d.year(); // OK d.add_year(1); // OK int j = cd.year(); // OK cd.add_year(1); // error : cannot change value of a const Date}
(2) 物理不变性和逻辑不变性(physical and logical constness)
Logical constness: to a user, the function appears not to change the state of its object, but some detail that the user cannot directly observe is updated.
逻辑不变性:对于用户来说,一个函数似乎没有修改其对象的状态,但一些用户不能直接观察的细节得到了更新。
class Date {public: // ... string string_rep() const; // string representationprivate: bool cache_valid; string cache; void compute_cache_value(); // fill cache // ...};例如Date内储存的年月日是整形,现在string_rep()要返回字符串格式的日期,重复的将字符串转化为日期可能是一个昂贵的操作,所以一个可行的解决方案是用cache储存字符串格式的日期,当调用string_rep()时返回cahce。从用户的角度,string_rep()并没有改变Date数据,所以它明显应该是const函数,但cache和cache_valid却有时会改变,这个问题可以野蛮的用const_cast解决,但下节将介绍一个更优雅的方法。
(3) mutable
我们可以把类的成员声明为mutable,这意味着const成员函数也能修改他们。
class Date {public: // ... string string_rep() const; // string representationprivate: mutable bool cache_valid; mutable string cache; void compute_cache_value() const; // fill (mutable) cache // ...};于是现在string_rep的实现就显而易见了
string Date::string_rep() const{ if (!cache_valid) { compute_cache_value(); cache_valid = true; } return cache;}(4) 间接可变性
声明mutable变量适用于小对象里的小部分数据,当情况很复杂时,更好的办法是把这些数据分离到另外的对象,进行间接存取
上面的例子将变为
struct cache { bool valid; string rep;};class Date {public: // ... string string_rep() const; // string representationprivate: cache∗ c; // initialize inconstr uctor void compute_cache_value() const; // fill what cache refers to// ...};string Date::string_rep() const{ if (!c−>valid) { compute_cache_value(); c−>valid = true; } return c−>rep;}
const并不会作用于通过指针和引用存取的成员对象,即仅指针的指向不变,指针指向的内容可变。
5. 自引用(Self-Reference)
对于一组相关的更新操作,返回被更新对象的引用往往非常有用,因为这使各个操作可以连接起来。
void f(Date& d){ // ... d.add_day(1).add_month(1).add_year(1); // ...}在所有非static的成员函数中,关键字this指向调用此成员函数的对象。对于类X,this的类型是X*。不过一般认为this是右值,即它不能被取址或者赋值。在const成员函数中,this的类型是const X*。this的绝大部分使用时隐式的,所有非static成员函数依赖于隐式的使用this来获取与其对应的对象(默认前缀this->)。
一个常见的显示使用this的例子是链表操作:
struct Link { Link∗ pre; Link∗ suc; int data; Link∗ insert(int x) // inser t x before this { return pre = new Link{pre, this, x}; } void remove() // remove and destroy this { if (pre) pre−>suc = suc; if (suc) suc−>pre = pre; delete this; } // ...};
6. 静态成员
静态成员变量:A variable that is part of a class, yet is not part of an object of that class, is called a static member.
静态成员函数:A function that needs access to members of a class, yet doesn’t need to be invoked for a particular object, is called a static member function.
上文的类内初始化依赖于全局变量today,可能导致Date在上下文不同时无法使用。
下面的默认初始化不依赖于全局变量:
class Date { int d, m, y; static Date default_date;public: Date(int dd =0, int mm =0, int yy =0); // ... static void set_default(int dd, int mm, int yy); // set default_date to Date(dd,mm,yy)};// We can now define the Date constructor to use default_date like this:Date::Date(int dd, int mm, int yy){ d = dd ? dd :default_date.d; m = mm ? mm : default_date.m; y = yy ? yy : default_date.y; // ... check that the Date is valid ...}
静态成员可以像其他成员一样被引用/调用,而且还可以在不提及对象的情况下被调用:
void f(){ Date::set_default(4,5,1945); // call Date’s static member set_default()}静态成员在使用前必须被定义,包括成员变量:
Date Date::default_date {16,12,1770}; // definition of Date::default_datevoid Date::set_default(int d, int m, int y) // definition of Date::set_default{ default_date = {d,m,y}; // assign new value to default_date}注意到此时Date{ }成为了Date::default_date的值的记号,所以我们不需要用另外的函数来读取默认值,当没有歧义是,仅仅用{ }就足够了:
void f1(Date);void f2(Date);void f2(int);void g(){ f1({}); // OK: equivalent to f1(Date{}) f2({}): // error : ambiguous: f2(int) or f2(Date)? f2(Date{}); // OK}
在多线程程序中,static成员数据需要用锁或者某种存取规则来避免竞争条件(Race condition)。
7. 成员类型(Member Types)
类型或者类型别名可以作为类的成员:
template<typename T>class Tree { using value_type = T; // member alias enum Policy { rb, splay, treeps }; // member enum class Node { // member class Node∗ right; Node∗ left; value_type value; public: void f(Tree∗); }; Node∗ top;public: void g(const T&); // ...};成员类可以使用外围类的static成员和类型,但仅当为其提供一个外部类的对象时才能使用外围类的非静态成员,即内部类有权存取外围类的成员,包括私有成员,但它并不了解当前的外围类对象。
template<typename T>void Tree::Node::f(Tree∗ p){ top = right; // error : no object of type Tree specified p−>top = right; // OK value_type v = left−>value; // OK: value_type is not associated with an object}但外围类对内部类没有特殊的存取权限:
template<typename T>void Tree::g(Tree::Node∗ p){ value_type val = right−>value; // error : no object of type Tree::Node value_type v = p−>right−>value; // error : Node::r ight is private p−>f(this); // OK}
成员类更多是一种概念上的便利而不是一个重要的基础特性
成员别名非常重要,因为它是泛型编程中关联类型的根基
成员枚举常常是枚举类的替代,它避免了枚举名污染外围域- C++11类(1) 基础技巧 Class Basic
- 基础练习 BASIC-1 闰年判断(c语言)
- c++class(类)
- C++基础---类(class)
- Return to the Basic - 类 (class )
- C语言入门第一讲(Basic基础)
- TPL文法(1):基础文法(TPL Basic Syntax)
- Java basic ---Class
- Java(反射的基础Class类)
- 反射基础Class类
- Java基础-Class类
- java基础------>Class类
- Python基础-class类
- C语言基础、函数、技巧
- 3.3.6 - [basic.scope.class] - 【基本.作用域.类】
- Some basic knowledges about the class Object (1)
- (基础) class 与 c 语言对比学习-OC+class
- Objective-C基础之@class与#import
- Norsar 3D v5.44 Linux 1CD(功能强大的正演软件)
- python执行shell命令
- mysql 对于一个库的备份和还原, 对于大数据量的快速备份和还原
- shell 13问
- 更新admob ios sdk GoogleAdMobAdsSdkiOS 时编译报错
- C++11类(1) 基础技巧 Class Basic
- JS面向对象的数据属性的用法
- declare-styleable中format详解
- 11--黑马程序员--技术总结之字符串
- Landmark EDM/EDT R5000.1.10.2
- myeclipse10 激活详细过程
- pcie--调试(一)
- 用读XML替代读取汉字字模文件
- protobuf协议语言指南