《新标准C++程序设计》3.3-3.4(C++学习笔记7)
1、构造函数、析构函数和变量的生存期
构造函数在对象生成时会被调用,析构函数在对象消亡时会被调用。
程序示例分析:
(1)
#include<iostream> using namespace std; class Demo { int id; public: Demo(int i) { //类型构造函数 id = i; cout << "id=" << id << " constructed" << endl; } ~Demo() { //析构函数 cout << "id=" << id << " destructed" << endl; } }; Demo d1(1); //全局变量,在main之前初始化 void Func() { static Demo d2(2);//静态局部变量,func结束消亡 Demo d3(3); cout << "func" << endl; } int main() { Demo d4(4); d4 = 6; cout << "main" << endl; { Demo d5(5); //局部变量的作用域和生存期都只到离它最近的,且将其包含在内的那一对“{}”中的“}”为止 } Func(); //引发析构函数 cout << "main ends" << endl; return 0; }
输出结果:
id=1 constructed id=4 constructed id=6 constructed id=6 destructed main id=5 constructed id=5 destructed id=2 constructed id=3 constructed func id=3 destructed main ends id=6 destructed id=2 destructed id=1 destructed
(2)
#include <iostream> using namespace std; class CMyclass { public: CMyclass() {}; CMyclass( CMyclass & c) { cout << "copy constructor" << endl; } ~CMyclass() { cout << "destructor" << endl; } };
输出结果:
copy constructor fun destructor //参数消亡 test copy constructor destructor // 返回值临时对象消亡 destructor // 局部变量消亡 destructor // 全局变量消亡
2、复制构造函数在不同编译器中的表现
class A { public: int x; A(int x_):x(x_) { cout << x << " constructor called" << endl; } A(const A & a ) { //本例中dev需要此const其他编译器不要 x = 2 + a.x; cout << "copy called" << endl; } ~A() { cout << x << " destructor called" << endl; } }; A f( ){ A b(10); return b; } int main( ){ A a(1); a = f(); return 0; }
Visual Studio输出结果:
1 constructor called 10 constructor called copy called 10 destructor called 12 destructor called 12 destructor called
Dev C++输出结果:
1 constructor called 10 constructor called 10 destructor called 10 destructor called
说明Dev出于优化目的并未生成返回值临时对象。 VS无此问题
3、静态成员变量和静态成员函数
(1)概念
静态成员:在说明前面加了static关键字的成员。
class CRectangle { private: int w, h; static int nTotalArea; //静态成员变量 static int nTotalNumber; //静态成员变量 public: CRectangle(int w_, int h_); ~CRectangle(); static void PrintTotal(); //静态成员函数 };
普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享。普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。因此静态成员不需要通过对象就能访问。
静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。静态成员函数本质上是全局函数。设置静态成员这种机制的目的是将和某些类紧密相关的全局变 量和函数写到类里面,看上去像一个整体,易于维护和理解。
sizeof 运算符不会计算静态成员变量。
class CMyclass { int n; static int s; };
sizeof( CMyclass ) 等于 4
(2)如何访问静态成员
①类名::成员名
CRectangle::PrintTotal();
②对象名.成员名
CRectangle r;
r.PrintTotal();
③指针->成员名
CRectangle * p = &r;
p->PrintTotal();
④引用.成员名
CRectangle & ref = r; int n = ref.nTotalNumber;
(3)静态成员示例
考虑一个需要随时知道矩形总数和总面积的图形处理程序可以用全局变量来记录总数和总面积用静态成员将这两个变量封装进类中,就更容易理解和维护
#include<iostream> using namespace std; class CRectangle { private: int w, h; static int nTotalArea; static int nTotalNumber; public: CRectangle(int w_, int h_); ~CRectangle(); static void PrintTotal(); }; CRectangle::CRectangle(int w_, int h_) { w = w_; h = h_; nTotalNumber++; nTotalArea += w * h; } CRectangle::~CRectangle() { nTotalNumber--; nTotalArea -= w * h; } void CRectangle::PrintTotal() { cout << nTotalNumber << "," << nTotalArea << endl; } int CRectangle::nTotalNumber = 0; int CRectangle::nTotalArea = 0; // 必须在定义类的文件中对静态成员变量进行一次说明 //或初始化。否则编译能通过,链接不能通过。 int main() { CRectangle r1(3, 3), r2(2, 2); //cout << CRectangle::nTotalNumber; // Wrong , 私有 CRectangle::PrintTotal(); r1.PrintTotal(); return 0; }
输出结果:
2,13 2,13
此CRectangle类写法, 有何缺陷?
在使用CRectangle类时,有时会调用复制构造函数生成临时的隐藏的CRectangle对象
调用一个以CRectangle类对象作为参数的函数时
调用一个以CRectangle类对象作为返回值的函数时
临时对象在消亡时会调用析构函数,减少nTotalNumber 和 nTotalArea的值,可是这些临时对象在生成时却没有增加 nTotalNumber 和 nTotalArea的值。
解决办法:为CRectangle类写一个复制构造函数。
CRectangle :: CRectangle(CRectangle & r ) { w = r.w; h = r.h; nTotalNumber ++; nTotalArea += w * h; }
(4)为什么在静态成员函数中,不能访问非静态成员变量, 也不能调用非静态成员函数?
void CRectangle::PrintTotal() { cout << w << "," << nTotalNumber << "," <<nTotalArea << endl; //wrong } CRetangle::PrintTotal(); //解释不通,w 到底是属于那个对象的?