从零开始学C++之类与对象:类声明、类作用域、前向声明、this指针、嵌套类、PIMPL 技法 等
2016-10-25 15:18
337 查看
一、类声明
//类是一种用户自定义类型,声明形式:
在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。
在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。
在关键字protected后面声明,与private类似,其差别表现在继承与派生时对派生类的影响不同。
假设定义了一个Clock 类,因为成员是private的,那么 Clock ck; ck.hour_ = 12; 是错误的,对此定义一个public 的void SetHour(int hour) 来设置hour_ 的值。
二、内联成员函数、成员函数的重载及其缺省参数
在这里有内联函数的概念。成员函数也可以是内联的,若在类内部实现,inline 关键字可加可不加;在类外部实现,需加inline,
如 inline void Clock::SetHour(int hour) { } 。实际上即使加了inline也不一定宏展开,比如遇到switch,for 语句的时候就往往不会。
此外,成员函数也像一般函数那样可以重载,也可以有缺省参数,参考这里。
三、类与结构体
class与struct的区别:在未指定访问权限时,class默认的是私有的,struct默认是公有的,
此外,Test 可以独立作为一个tag,而不像C语言那样需要 struct Test 作为一个类型。
四、隐含的 this 指针
成员函数有一个隐含的附加形参,即指向该对象的指针,这个隐含的形参叫做this指针(编译器自动传递)
使用this指针保证了每个对象可以拥有不同数值的数据成员,但处理这些成员的代码可以被所有对象共享
成员函数是只读的代码,由所有对象共享,并不占对象的存储空间,因为this指针指向当前对象,所以成员函数可以区分它所作用的对象是哪一个。
(哪个对象调用了this所在的函数,this就代表哪个对象)
再来看一道经典的题目:
可以理解为如下的C代码:
相当于成员函数传递的this指针为0,那调用会出错吗? 肯定是正确输出”A” 的,因为this为0 表示没有对某个对象进行操作,而print里面确实没有对某
个对象成员进行操作,所以是可以运行的。
五、类作用域、前向声明
(1)、每个类都定义了自己的作用域称为类作用域,类作用域中说明的标识符只在类中可见。除了类作用域,还有块作用域、文件作用域、函数原型作用域、函数作用域,举个例子:
(2)、C++中类必须先定义,才能够实例化。两个类需要相互引用头文件形成一个“环形”引用时会出错。这时候需要用到前向声明,前向声明的类不能实例,但可以定义指针或引用。
六、嵌套类、局部类
(1)、嵌套类
外围类需要使用嵌套类对象作为底层实现,并且该嵌套类只用于外围类的实现,且同时可以对用户隐藏该底层实现。
从作用域的角度看,嵌套类被隐藏在外围类之中,该类名只能在外围类中使用。如果在外围类之外的作用域使用该类名时,需要加名字限定。
嵌套类中的成员函数可以在它的类体外定义。
嵌套类的成员函数对外围类的私有成员没有访问权,反之亦然。
嵌套类仅仅只是语法上的嵌入
(2)、局部类
类也可以定义在函数体内,这样的类被称为局部类(local class)。局部类只在定义它的局部域内可见。
局部类的成员函数必须被定义在类体中。
局部类中不能有静态成员,关于类中的静态成员和静态成员函数以后再谈。
七、PIMPL 技法
来看下面的示例:
上面程序存在的问题是:
1、引入更多的头文件,降低编译速度
2、在编译期如果X的大小改变了,y.cpp 和 main.cpp 都得重新编译;在运行期,如果X有子类,也不能使用多态虚函数。
3、假设y.cpp 编译成动态库给main.cpp 使用,当X的大小变化,动态库需要重新编译,此时main.cpp 因为有定义对象y ,故也需要
重新编译。
下面介绍一种PIMPL 技法,有人也把它当作一种设计模式:
PIMPL(private implementation或pointer to implementation)也称为handle/body idiom
PIMPL背后的思想是把客户与所有关于类的私有部分的知识隔离开。避免其它类知道其内部结构
降低编译依赖、提高重编译速度
接口和实现分离
降低模块的耦合度
编译期
运行期
提高了接口的稳定程度
对于库的使用,方法不用改变
对于库的编译,动态库的变更,客户程序不用重新编译
修改后的程序:
即Y 内部成员是X* 指针,在32位系统上,指针大小固定为4个字节,即使X大小改变,也不影响Y。如果X 有子类,通过基类指针px_
还可以实现虚函数多态。
参考:
C++ primer 第四版
Effective C++ 3rd
C++编程规范
转载自http://blog.csdn.net/jnu_simba/article/details/9156427
//类是一种用户自定义类型,声明形式:
class 类名称 { public: 公有成员(外部接口) private: 私有成员 protected: 保护成员 };
在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。
在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。
在关键字protected后面声明,与private类似,其差别表现在继承与派生时对派生类的影响不同。
class Clock { public: void Display(); void Init(int hour, int minute, int second); private: int hour_; int minute_; int second_; };
假设定义了一个Clock 类,因为成员是private的,那么 Clock ck; ck.hour_ = 12; 是错误的,对此定义一个public 的void SetHour(int hour) 来设置hour_ 的值。
二、内联成员函数、成员函数的重载及其缺省参数
在这里有内联函数的概念。成员函数也可以是内联的,若在类内部实现,inline 关键字可加可不加;在类外部实现,需加inline,
如 inline void Clock::SetHour(int hour) { } 。实际上即使加了inline也不一定宏展开,比如遇到switch,for 语句的时候就往往不会。
此外,成员函数也像一般函数那样可以重载,也可以有缺省参数,参考这里。
三、类与结构体
class与struct的区别:在未指定访问权限时,class默认的是私有的,struct默认是公有的,
struct Test { int X;//公有的 ... };
此外,Test 可以独立作为一个tag,而不像C语言那样需要 struct Test 作为一个类型。
四、隐含的 this 指针
成员函数有一个隐含的附加形参,即指向该对象的指针,这个隐含的形参叫做this指针(编译器自动传递)
使用this指针保证了每个对象可以拥有不同数值的数据成员,但处理这些成员的代码可以被所有对象共享
成员函数是只读的代码,由所有对象共享,并不占对象的存储空间,因为this指针指向当前对象,所以成员函数可以区分它所作用的对象是哪一个。
(哪个对象调用了this所在的函数,this就代表哪个对象)
再来看一道经典的题目:
class A { public: int m; void print() { cout << "A" << endl; } }; A *pa = 0; pa->print();
可以理解为如下的C代码:
void print(A *this) { cout << "A" << endl; } A *pa = 0; print_A(pa);
相当于成员函数传递的this指针为0,那调用会出错吗? 肯定是正确输出”A” 的,因为this为0 表示没有对某个对象进行操作,而print里面确实没有对某
个对象成员进行操作,所以是可以运行的。
五、类作用域、前向声明
(1)、每个类都定义了自己的作用域称为类作用域,类作用域中说明的标识符只在类中可见。除了类作用域,还有块作用域、文件作用域、函数原型作用域、函数作用域,举个例子:
#include <iostream> using namespace std; class Test { public: int num_; }; //num_ = 20; Error,num_的作用域在类内部 int num_ = 20; // num_的作用域是文件作用域,与类中的num_是不同的作用域 int add(int a, int b); // a, b两个标识符的作用域为函数原型作用域 int main(void) { int num_ = 30; // num_为块作域 { int num_ = 100; // num_为块作域 } cout << num_ << endl; cout <<::num_ << endl; return 0; } int add(int a, int b) // 形参a与b也算是块作用域 { return a + b; } int test() { LABEL1: //函数作用域 cout << "label1" << endl; goto LABEL3; LABEL2: cout << "label2" << endl; goto LABEL1; LABEL3: cout << "label3" << endl; goto LABEL2; }
(2)、C++中类必须先定义,才能够实例化。两个类需要相互引用头文件形成一个“环形”引用时会出错。这时候需要用到前向声明,前向声明的类不能实例,但可以定义指针或引用。
#ifndef _B_H_ #define _B_H_ class A; class B { public: B(void); ~B(void); void fun(A &a) { } A *a_; // 前向声明的类不能实例化对象 }; #endif // _B_H_
#ifndef _A_H_ #define _A_H_ #include "B.h" class A { public: A(void); ~A(void); B b_; }; #endif // _A_H_
六、嵌套类、局部类
(1)、嵌套类
外围类需要使用嵌套类对象作为底层实现,并且该嵌套类只用于外围类的实现,且同时可以对用户隐藏该底层实现。
从作用域的角度看,嵌套类被隐藏在外围类之中,该类名只能在外围类中使用。如果在外围类之外的作用域使用该类名时,需要加名字限定。
嵌套类中的成员函数可以在它的类体外定义。
嵌套类的成员函数对外围类的私有成员没有访问权,反之亦然。
嵌套类仅仅只是语法上的嵌入
(2)、局部类
类也可以定义在函数体内,这样的类被称为局部类(local class)。局部类只在定义它的局部域内可见。
局部类的成员函数必须被定义在类体中。
局部类中不能有静态成员,关于类中的静态成员和静态成员函数以后再谈。
#include <iostream> using namespace std; class Outer { public: class Inner { public: void Fun(); //{ // cout<<"Inner::Fun ..."<<endl; //} }; public: Inner obj_; void Fun() { cout << "Outer::Fun ..." << endl; obj_.Fun(); } }; void Outer::Inner::Fun() { cout << "Inner::Fun ..." << endl; } void Fun() { class LocalClass { public: int num_; void Init(int num) { num_ = num; } void Display() { cout << "num=" << num_ << endl; } //static int num2_; // 局部类内部不能定义静态成员 }; LocalClass lc; lc.Init(10); lc.Display(); } int main(void) { Outer o; o.Fun(); Outer::Inner i; i.Fun(); Fun(); //LocalClass lc; Error,局部类只能在定义它的函数体中使用 return 0; }
七、PIMPL 技法
来看下面的示例:
// file y.h #include "x.h" class Y { void Fun(); X x_; }; // file y.cpp #include "y.h" void Y::Fun { return x_.Fun(); } // file main.cpp #include “y.h” int main(void) { Y y; y.Fun(); }
上面程序存在的问题是:
1、引入更多的头文件,降低编译速度
2、在编译期如果X的大小改变了,y.cpp 和 main.cpp 都得重新编译;在运行期,如果X有子类,也不能使用多态虚函数。
3、假设y.cpp 编译成动态库给main.cpp 使用,当X的大小变化,动态库需要重新编译,此时main.cpp 因为有定义对象y ,故也需要
重新编译。
下面介绍一种PIMPL 技法,有人也把它当作一种设计模式:
PIMPL(private implementation或pointer to implementation)也称为handle/body idiom
PIMPL背后的思想是把客户与所有关于类的私有部分的知识隔离开。避免其它类知道其内部结构
降低编译依赖、提高重编译速度
接口和实现分离
降低模块的耦合度
编译期
运行期
提高了接口的稳定程度
对于库的使用,方法不用改变
对于库的编译,动态库的变更,客户程序不用重新编译
修改后的程序:
// file y.h class X; class Y { Y(); ~Y(); void Fun(); X *px_; }; // file y.cpp #include "x.h" Y::Y() : px_( new X ) {} Y::~Y() { delete px_; px_ = 0; } void Y::Fun() { return px_->Fun(); } // file main.cpp #include "y.h" int main(void) { Y y; y.Fun(); }
即Y 内部成员是X* 指针,在32位系统上,指针大小固定为4个字节,即使X大小改变,也不影响Y。如果X 有子类,通过基类指针px_
还可以实现虚函数多态。
参考:
C++ primer 第四版
Effective C++ 3rd
C++编程规范
转载自http://blog.csdn.net/jnu_simba/article/details/9156427
相关文章推荐
- 从零开始学C++之类与对象:类声明、类作用域、前向声明、this指针、嵌套类、PIMPL 技法 等
- 从零开始学C++之类与对象:类声明、类作用域、前向声明、this指针、嵌套类、PIMPL 技法 等
- 类声明、类作用域、前向声明、this指针、嵌套类、PIMPL 技法 等
- C++ 基类、派生类对象指针的声明与使用
- 从零开始学C++之虚函数与多态(一):虚函数表指针、虚析构函数、object slicing与虚函数、C++对象模型图
- 从零开始学C++之对象的使用(二):四种对象生存期和作用域、static 用法总结
- 基类中this指针和派生类对象的关系,同名隐藏原则和作用域的示例
- C++之指针(对象指针+对象成员指针+this指针[待补充])
- c++之this指针做参数实现对象间的交互实例
- C++ 对象和类(十七)--operator+、private、public、protected、this->指针
- 对象和类,公有类和私有类,构造函数和析构函数,const成员函数,this指针,对象数组,类作用域,抽象数据
- c++;对象引用前加const报错:不能将“this”指针从“const a”转换为“a &”
- C++ 常成员函数和常对象以及this指针的重要理解
- c++对象数组和this指针
- C++:对象数组与this指针
- 从零开始学C++之对象的使用(二):四种对象生存期和作用域、static 用法总结
- 从零开始学C++之对象的使用(二):四种对象生存期和作用域、static 用法总结
- 从零开始学C++之虚函数与多态(一):虚函数表指针、虚析构函数、object slicing与虚函数、C++对象模型图
- C++ 类 & 对象-C++ 内联函数-C++ this 指针-C++ 类的静态成员
- c++的类中,声明一个对象好还是用指针申请一块空间好?