龙空技术网

C++个人笔记总结

知识分享者185 230

前言:

现在我们对“mfc office”大致比较珍视,姐妹们都需要知道一些“mfc office”的相关资讯。那么小编在网络上收集了一些关于“mfc office””的相关内容,希望同学们能喜欢,姐妹们一起来学习一下吧!

C++

个人笔记总结

---------------------------------

---------------

Good luck

Believe yourself

Just go

概述:

一、C++语言语法基础(6)

1.从C到C++的过渡

2.类和对象

3.操作符重载

4.继承与多态

5.异常和I/O流

二、数据结构和算法

1.基本数据结构,堆栈、队列、链表、二叉树,实现和应用(2)

2.排序和查找算法

三、模板和STL

1.模板语法

2.STL

四、阶段项目

简化的企业管理信息系统(MIS)

第一课 从C到C++的过渡

一、背景介绍

算盘 - 面向硬件的编程

电子计算机 - 机器语言的编程1010

- 汇编语言的编程ADD

- 高级语言的编程Fortran

printf ("%d", 12);

- 结构化程序设计C/PASCL

顺序、分支、循环、函数

- 面向对象的程序设计C++/Java/C#

- 面向问题的程序设计

1960 - Algol 60,算法语言,远离硬件,不适合进行系统开发

1963 - 剑桥大学,CPL,在Algol 60的基础上增加对系统开发的支

持,复杂,不易掌握,不易使用

1970 - MIT,BCPL,CPL的精华版,易学易用,太慢,不实用

1972 - Ken Thomposon,B语言,通过运行时支持优化BCPL的性能,缺少类型

1973 - Dennis Ritchie,C语言,用C重新实现UNIX内核

1978 - 《The C Programming Language》,第一个C语言的事实标准

1989 - ANSI C,C89

1990 - ISO C, C90

1999 - ISO C 修订,C99

197X - Bajarne Stroustrup,simula早期的面向对象语言,性能低

下,B语言。

1979 - 贝尔实验室,多核UNIX系统仿真,Cpre,

通过扩展宏为C语言增加类似simula的面向对象机制。C with Class:

simula - 类

Alogo 68 - 操作符重载

Ada - 模板、名字空间

Smalltalk - 引用、异常

C是C++的子集,C++是对C的扩展。

1983 - C++命名

1985 - CFront 1.0,第一款商用C++编译器

1987 - GNU C++

1990 - Borland C++

1992 - Microsoft C++,IBM C++

1998 - ISO C++98

2003 - ISO C++03

2011 - ISO C++2011/C++11/C++0x

二、C++语言的使用领域:

1.游戏开发:强建模能力,性能高。

2.科学计算:FORTRAN,C++算法库。

3.网络和分布式:ACE框架。

4.桌面应用:VC/MFC,Office,QQ,多媒体

5.操作系统和设备驱动:优化编译器的发明使C++在底层开发方面可

以和C向媲美。

6.移动终端

既需要性能,同时又要有面向对象的建模。

三、C++比C更丰富

1.支持面向对象,将问题域和方法域统一化。宏观面向对象,微观面

向过程。

2.支持泛型编程。

int add (int a, int b) { ... }

template<typename T>

T add (T a, T b) { ... }

3.支持异常机制。

int func (void) {

...

}

int main (void) {

if (func () == -1) {

错误处理;

}

}

操作符重载

四、第一个C++程序

1.编译器:g++,如果用gcc需要带上-lstdc++,

指定其使用标准c++的运行库。

2.源文件扩展名:.cpp/.cc/.C/.cxx/.c++,最好用.cpp

3.头文件:#include <iostream>

大多数标准库头文件都没有.h后缀。

4.输出:cout - 标准输出对象

输入:cin - 标准输入对象

插入运算符:<<

提取运算符:>>

5.std:所有标准库的函数、对象、类型都位于std名字空间中。

五、名字空间

1.对程序中的标识符(类型、函数、变量),

按照某种逻辑规则划分成若干组。

2.定义名字空间

namespace 名字空间名 {

名字空间成员;

}

3.使用名字空间

1作用于限定符:名字空间名::名字空间成员,

表示访问特定名字空间中的特定成员。

例子:

#include <iostream>

int main (void) {

std::cout << "Hello, World !" << std::endl;

int i;

double d;

char s[256];

//scanf ("%d%lf%s", &i, &d, s);

std::cin >> i >> d >> s;

//printf ("%d %lf %s\n", i, d, s);

std::cout << i << ' ' << d << ' ' << s << '\n';

return 0;

}

---------------------------------------------------------

2名字空间指令:

using namespace 名字空间名;

在该条指令之后的代码对指令所指名字空间中的所有成员都可见,

可直接访问这些成员,无需加"::"。

例:using namespace std;

3名字空间声明:

using 名字空间名::名字空间成员;

将指定名字空间中的某个成员引入当前作用域,

可直接访问这些成员,无需加"::"。

4.匿名名字空间

如果一个标识符没有被显示地定义在任何名字空间中,

编译器会将其缺省地置于匿名名字空间中。

对匿名名字空间中的成员通过"::名字空间成员"的形式访问。

5.名字空间合并

6.名字空间嵌套

namespace ns1 {

namespace ns2 {

namespace ns3 {

void foo (void) { ... }

}

}

}

ns1::ns2::ns3::foo ();

using namespace ns1::ns2::ns3;

foo ();

---------------

例子:名字空间

#include <iostream>

using namespace std;

//namespace {

void print (int money) {

cout << money << endl;

}

//}

// 农行名字空间

namespace abc {

int balance = 0;

void save (int money) {

balance += money;

}

void draw (int money) {

balance -= money;

}

}

namespace abc {

void salary (int money) {

balance += money;

}

void print (int money) {

cout << "农行:";

::print (money);

}

}

// 建行名字空间

namespace ccb {

int balance = 0;

void save (int money) {

balance += money;

}

void draw (int money) {

balance -= money;

}

void salary (int money) {

balance += money;

}

}

int main (void) {

using namespace abc; // 名字空间指令

save (5000);

cout << "农行:" << balance << endl;

draw (3000);

cout << "农行:" << balance<< endl;

ccb::save (8000);

cout << "建行:" << ccb::balance << endl;

ccb::draw (5000);

cout << "建行:" << ccb::balance << endl;

using ccb::salary; // 名字空间声明

//using abc::salary;

salary (6000);

cout << "建行:" << ccb::balance << endl;

abc::print (abc::balance);

return 0;

}

六、C++中的结构、联合和枚举

1.结构

和C语言的不同:

1)定义结构型变量时,可以省略struct关键字。

2)结构内部可以定义函数——成员函数。

3)sizeof (空结构) -> 1

例子:

#include <iostream>

using namespace std;

struct Student {

char name[128];

int age;

void who (void) { //成员函数

cout << "我叫" << name << ",今年" << age

<< "岁了。" << endl;

}

};

int main (void) {

Student student = {"张飞", 25}, *ps = &student;

student.who ();

ps->who ();

struct A {};

cout << sizeof (A) << endl;

return 0;

}

-----------------------------------------------------------

2.联合

增加了匿名联合的概念。借用联合语法形式,

描述一些变量在内存中的布局方式。

int main()

{

UNION

{

int a;

char ch[4];

};

a=0x12345678;

}

定义联合变量时,可以不加union

例子:

#include <iostream>

using namespace std;

int main (void) {

// 匿名联合

union {

int x;

char c[4] /*= {'A', 'B', 'C', 'D'}*/;

};

cout << (void*)&x << ' ' << (void*)c << endl;

x = 0x12345678;

for (int i = 0; i < 4; ++i)

cout << hex << (int)c[i] << ' ';

cout << endl;

return 0;

}

---------------------------------------------------

3.枚举

枚举是一个独立的数据类型。

C:

enum E {a, b, c};

enum E e;

e = a;

e = 1000;

C++:

enum E {a, b, c};

E e;

e = a;

e = b;

e = c;

b=1; // ERROR !

e = 1000; // ERROR !

e = 1; // ERROR !

例子:

#include <iostream>

using namespace std;

int main (void) {

enum E {a, b, c};

E e;

e = a;

e = b;

e = c;

//e = 1000;//报错

//e = 1; //报错 return 0;}

七、C++的布尔类型,跟在cout后面可以boolalpha

bool b = true;

b = false;

cout << sizeof (b) << endl; // 1

b = 100;

b = 1.234;

b = "hello";

b = 'A';

八、C++中的运算符别名

&& - and

|| - or

& - bitand

^ - xor

{ - <%

} - %>

[ - <:

] - :>

九、C++中的函数

1.重载:条件

在同一个作用域中,

函数名相同,

参数表不同的函数,

构成重载关系。

C++编译器会对程序中的函数做换名,

将参数表中的类型信息汇合到函数名中,以保证函数名的唯一。

通过extern "C",可以要求编译器不做C++换名,以方便在C语言的模块中使用C++编译生成的代码。

方式一:

extern "C" {

int add (int a, int b) {

return a + b;

}

int sub (int a, int b) {

return a - b;

}

}

方式二:

extern "C" int add (int a, int b, int c) {

return a + b + c; }

2.缺省参数和哑元参数

1)如果调用一个函数时,没有提供实参,那么对应形参就取缺省值。

2)如果一个参数带有缺省值,那么它后边的所有参数必须都带有缺省值。

3)如果一个函数声明和定义分开,那么缺省参数只能放在声明中。

4)避免和重载发生歧义。

5)只有类型而没有名字的形参,谓之哑元。

i++ - operator++

++i

V1: void decode (int arg) { ... }

V2: void decode (int) { ... }

例子1:重载与缺省值

#include <iostream>

using namespace std;

void foo (int a = 10, double b = 0.01,

const char* c = "tarena"); //函数1

void foo (void) {} //函数2

//函数1与函数2构成重载关系

void bar (int) { //函数3

cout << "bar(int)" << endl;

}

void bar (int, double) { //函数4

cout << "bar(int,double)" << endl;

}

//函数3与函数4构成重载关系

int main (void) {

foo (1, 3.14, "hello");//调用函数1

foo (1, 3.14); //调用函数1

foo (1); //调用函数1

//foo (); // 歧义 ,可以调用函数2,但也可以调用函数1,因为函数1在不提供实参的情况下,可以取缺省值。

bar (100); //调用函数3

bar (100, 12.34); //调用函数4

return 0;

}

--------------------------------------

例子2:重载与作用域

#include <iostream>

using namespace std;

namespace ns1 {

int foo (int a) { 函数1

cout << "ns1::foo(int)" << endl;

return a;

}

};

namespace ns2 {

double foo (double a) { 函数2

cout << "ns2::foo(double)" << endl;

return a;

}

};

int main (void) {

using namespace ns1; // 名字空间指令

using namespace ns2; // 名字空间指令

cout << foo (10) << endl; //10 调用函数1,作用域可见ns2与ns1,所以与函数2构成重载

cout << foo (1.23) << endl; //1.23 调用函数2,作用域可见ns2与ns1,所以与函数1构成重载

using ns1::foo; //名字空间声明

(当同时出现名字指令与名字空间声明,则名字空间声明会隐藏名字空间指令)

cout << foo (10) << endl; //10,调用函数1,只可见名字空间ns1的foo(),所以也并不构成重载。

cout << foo (1.23) << endl; //10,调用函数1,只可见名字空间ns1的foo(),所以也并不构成重载。

using ns2::foo; //名字空间声明

cout << foo (10) << endl; //10,调用函数1,可见名字空间ns1与名字空间ns2的foo(),所以构成重载。

cout << foo (1.23) << endl; //1.23,调用函数2,可见名字空间ns1与名字空间ns2的foo(),所以构成重载。

return 0;

}

--------------------------------------------------------

3.内联

1)编译器用函数的二进制代码替换函数调用语句,减少函数调用的时间开销。这种优化策略成为内联。

2)频繁调用的简单函数适合内联,而稀少调用的复杂函数不适合内联。

3)递归函数无法内联。

4)通过inline关键字,可以建议编译对指定函数进行内联,但是仅仅是建议而已。

inline void foo (int x, int y){...}

十、C++的动态内存分配

malloc/calloc/realloc/free

1.new/delete:对单个变量进行内存分配/释放

2.new[]/delete[]:对数组进行内存分配/释放

例子:new与delete

#include <iostream>

using namespace std;

int main (void) {

//int* pi = (int*)malloc (sizeof (int));

//free (pi); //c中的方法

int* pi = new int;

*pi = 1000;

cout << *pi << endl;

delete pi; //一定要释放内存,否则会造成内存泄露,很严重

pi = NULL; //不要忘记这个,虽然不会报错,但是要有好习惯

/*

*pi = 2000;

cout << *pi << endl; //pi指向的内存地址已经被释放,被初始化为指向NULL

*/

pi = new int[10];

for (size_t i = 0; i < 10; ++i)

pi[i] = i;

for (size_t i = 0; i < 10; ++i)

cout << pi[i] << ' ';

cout << endl;

delete[] pi; //千万记住new[]要用delete[]来释放内存

pi = NULL;

pi = new int (1234); //用new分配内存的同时初始化赋一个值。

cout << *pi << endl; //1234

delete pi;

pi = NULL;

char buf[4] = {0x12,0x34,0x56,0x78};

pi = new (buf) int;

cout << hex << *pi << endl;

//delete pi;

cout << (void*)pi << ' ' << (void*)buf << endl;

int (*p)[4] = new int[3][4];

delete[] p;

int (*q)[4][5] = new int[3][4][5];

delete[] q;

return 0;

}

十一、引用

1.引用即别名。

int a = 20;

int& b = a; // int* b = &a;

b = 10; // *b = 10;

cout << a << endl; // 10

2.引用必须初始化。

int a;

int* p;

a = 20;

p = &a;

int& b; // ERROR !

int& b = a; // OK

3.引用一旦初始化就不能再引用其它变量。

int a = 20, c = 30;

int& b = a;

b = c; // c => b/a

4.引用的应用场景

1)引用型参数

a.修改实参

b.避免拷贝,通过加const可以防止在函数中意外地修改实参的值,同时还可以接受拥有常属性的实参。

2)引用型返回值

int b = 10;

int a = func (b);

func (b) = a;

从一个函数中返回引用往往是为了将该函数的返回值作为左值使用。但是,一定要保证函数所返回的引用的目标在该函数返回以后依然有定义,否则将导致不确定的后果。

不要返回局部变量的引用,可以返回全局、静态、成员变量的引用,也可以返回引用型形参变量本身。

5.引用和指针

1)引用的本质就是指针,很多场合下引用和指针可以互换。

2)在C++层面上引用和指针存在以下不同:

A.指针式实体变量,但是引用不是实体变量。

int& a = b;

sizeof (a); // 4

double& d = f;

sizeof (d); // 8

B.指针可以不初始化,但是引用必须初始化。

C.指针的目标可以修改,但是引用的目标的不能修改。

D.可以定义指针的指针,但是不能定义引用的指针。

int a;

int* p = &a;

int** pp = &p; // OK

int& r = a;

int&* pr = &r; // ERROR

E.可以定义指针的引用,但是不能定义引用的引用。

int a;

int* p = &a;

int*& q = p; // OK

int& r = a;

int&& s = r; // ERROR

F.可以定义指针的数组,但是不能定义引用的数组。

int a, b, c;

int* parr[] = {&a, &b, &c}; // OK

int& rarr[] = {a, b, c}; // ERROR

可以定义数组的引用。

int arr[] = {1, 2, 3};

int (&arr_ref)[3] = arr; // OK

例子:指针与引用

#include <iostream>

using namespace std;

void swap1 (int a, int b) {

int c = a;

a = b;

b = c;

}

void swap2 (int* a, int* b) {

int c = *a;

*a = *b;

*b = c;

}

void swap3 (int& a, int& b) {

int c = a;

a = b;

b = c;

}

void swap4 (const char* x, const char* y) {

const char* z = x;

x = y;

y = z;

}

void swap5 (const char** x, const char** y) {

const char* z = *x;

*x = *y;

*y = z;

}

void swap6 (const char*& x, const char*& y) {

const char* z = x;

x = y;

y = z;

}

struct Student {

char name[1024];

int age;

};

void print (const struct Student& s) {

cout << s.name << "," << s.age << endl;

//s.age = -1;

}

void foo (const int& x) {

cout << x << endl;

}

int main (void) {

int a = 100, b = 200;

//swap1 (a, b);

//swap2 (&a, &b);

swap3 (a, b);

cout << a << ' ' << b << endl; // 200 100

const char* x = "hello", *y = "world";

//swap4 (x, y);

//swap5 (&x, &y);

swap6 (x, y);

cout << x << ' ' << y << endl;

Student s = {"张飞", 22};

print (s);

print (s);

foo (100);

return 0;

}

十二、显示类型转换运算符

C:目标类型变量 = (目标类型)源类型变量;

1.静态类型转换

static_cast<目标类型> (源类型变量)

如果在目标类型和源类型之间某一个方向上可以做隐式类型转换,那么在两个方向上都可以做静态类型转换。反之如果在两个方向上都不能做隐式类型转换,那么在任意一个方向上也不能做静态类型转换。

int* p1 = ...;

void* p2 = p1;

p1 = static_cast<int*> (p2);

char c;

int i = c;

如果存在从源类型到目标类型的自定义转换规则,那么也可以使用静态类型转换。

例子:静态类型转换

#include <iostream>

using namespace std;

int main (void) {

int* p1 = NULL; //p1为int型的指针

void* p2 = p1; //p2为void型的指针

p1 = static_cast<int*> (p2); //将void型的p2指针转换为int型指针并复制给int型的p1指针。

return 0;

}

---------------------------------------------------

2.动态类型转换

dynamic_cast<目标类型> (源类型变量)

用在具有多态性的父子类指针或引用之间。

3.常类型转换

const_cast<目标类型> (源类型变量)

给一个拥有const属性的指针或引用去常

const int a = 100;

const int* p1 = &a;

*p1 = 200; // ERROR

int* p2 = const_cast<int*> (p1);

*p2 = 200; // OK

4.从解释类型转换

reinterpret_cast<目标类型> (源类型变量);

在不同类型的指针或引用之间做类型转换,以及在指针和整型之间做类型转换。

5.目标类型变量 = 目标类型(源类型变量);

int a = int (3.14);

例子一:常类型转换

#include <iostream>

using namespace std;

int main (void) {

const volatile int a = 100;

(关键字volatile在描述变量时使用,阻止编译器优化那些以valatile修饰的变量,volatile被用在一些变量能被意外方式改变的地方,例如:抛出中断,这些变量若无volatile可能会和编译器执行的优化 相冲突.)

//a = 200;

const volatile int* p1 = &a;

//*p1 = 200;

int* p2 = const_cast<int*> (p1); //去常,去掉常属性

*p2 = 200;

cout << *p2 << endl; // 200

cout << a << endl; // 200

// cout << 100 << endl;

return 0;

}

-------------------------------------------------------

例子二:解释型类型转换

#include <iostream>

using namespace std;

int main (void) {

int i = 0x12345678;

char* p = reinterpret_cast<char*> (&i);

for (size_t i = 0; i < 4; ++i)

cout << hex << (int)p[i] << ' '; //78 56 34 12

cout << endl;

float* q = reinterpret_cast<float*> (&i);

cout << *q << endl;

void* v = reinterpret_cast<void*> (i);

cout << v << endl;

return 0;

}

十三、C++之父的建议

1.少用宏,多用const、enum和inline

#define PAI 3.141519

const double PAI = 3.14159;

#define ERROR_FILE -1

#define ERROR_MEM -2

enum {

ERROR_FILE = -1,

ERROR_MEM = -2

};

#define max(a,b) ((a)>(b)?(a):(b))

inline int double max (double a, double b) {

return a > b ? a : b;

}

2.变量随用随声明同时初始化。

3.少用malloc/free,多用new/delete。

4.少用C风格的强制类型转换,多用类型转换运算符。

5.少用C风格的字符串,多用string。

6.树立面向对象的编程思想。

附加:string类:

例子:

#include <iostream>

#include <cstring>

#include <cstdio>

using namespace std;

int main (void) {

string s1 ("hello");

string s2 = "world";

(s1 += " ") += s2;

cout << s1 << endl;

string s3 = s1;

cout << s3 << endl;

cout << (s1 == s3) << endl;

s3[0] = 'H';

cout << s3 << endl;

cout << (s1 > s3) << endl;

cout << s1.length () << endl;

cout << (s1 == s3) << endl;

cout << (strcasecmp (s1.c_str (),

s3.c_str ()) == 0) << endl;

cout << sizeof (s1) << endl;

FILE* fp = fopen ("string.txt", "w");

//fwrite (&s3, sizeof (s3), 1, fp);

fwrite (s3.c_str (), sizeof (char),

s3.length (), fp);

fclose (fp);

return 0;

}

第二课 类和对象

一、什么是对象

1.万物皆对象

2.程序就是一组对象,对象之间通过消息交换信息

3.类就是对对象的描述和抽象,对象就是类的具体化和实例化

二、通过类描述对象

属性:姓名、年龄、学号

行为:吃饭、睡觉、学习

类就是从属性和行为两个方面对对象进行抽象。

三、面向对象程序设计(OOP)

现实世界 虚拟世界

对象 -> 抽象 -> 类 -> 对象

1.至少掌握一种OOP编程语言

2.精通一种面向对象的元语言—UML

3.研究设计模式,GOF

四、类的基本语法

1.类的定义

class 类名 {

};

class Student {

};

2.成员变量——属性

class 类名 {

类型 成员变量名;

};

class Student {

string m_name;

int m_age;

};

3.成员函数——行为

class 类名 {

返回类型 成员函数名 (形参表) {

函数体;

}

};

class Student {

string m_name;

int m_age;

void eat (const string& food) {

...

}

};

4.访问控制属性

1)公有成员:public,谁都可以访问。

2)私有成员:private,只有自己可以访问。

3)保护成员:protected,只有自己和自己的子类可以访问。

4)类的成员缺省访控属性为私有,而结构的成员缺省访控属性为公有。

例子:

#include <iostream>

using namespace std;

class Student {

private: //声明为私有部分

string m_name;

int m_age;

public: //声明为私有部分

void eat (const string& food) {

cout << m_age << "岁的" << m_name

<< "同学正在吃" << food << "。" << endl;

}

void setName (const string& name) { //为接口

if (name == "2")

cout << "你才" << name << "!" << endl;

else

m_name = name;

}

void setAge (int age) { //为接口

if (age < 0)

cout << "无效的年龄!" << endl;

else

m_age = age;

}

};

int main (void) {

Student student;

student.setName ("2"); //你才2

student.setAge (-100); //无效年龄

student.setName ("张飞"); //将其赋值给成员变量m_name

student.setAge (20); //将其赋值给成员变量m_age

student.eat ("包子"); //20岁的张飞同学正在吃包子

return 0;

}

5.构造函数

class{

...

类名 (行参表) {

构造函数体;

}

};

当一个对象被创建时,构造函数会自动被执行,其参数来自构造实参。

6.构造函数可以通过构造参数实现重载。

7.如果一个类没有定义任何构造函数,那么系统就会缺省地为其提供一个无参构造函数,该构造函数对于基本类型的成员变量不做初始化,对于类类型的成员变量,调用其相应类型的无参构造函数初始化。

8.对象创建过程

分配内存->调用构造函数->调用类类型成员的构造函数->构造函数的代码

9.初始化表

class 类名 {

类名(...) :初始化表 {

构造函数体

}

};

const int x = 100;

x = 100;

int& a = b;

a = b;

1)如果类中含有常量或引用型的成员变量,必须通过初始化表对其初始化。

2)成员变量的初始化顺序仅于其被声明的顺序有关,而与初始化表的顺序无关。

class A {

public:

A (char* psz) : m_str (psz),

m_len (m_str.length()) {}

private:

size_t m_len;

string m_str;

}

例子1:类的声明与定义以及使用可以不在一个文件

这是s.h文件

#ifndef _S_H

#define _S_H

#include <string>

using namespace std;

// 声明Student类

class Student {

public:

Student (const string& name = "", int age = 0);

void eat (const string& food);

private:

string m_name;

int m_age;

};

#endif // _S_H

这是s.cpp文件

#include <iostream>

using namespace std;

#include "s.h"

// 实现Student类

Student::Student (const string& name /* = "" */,

int age /* = 0 */) : m_name (name),

m_age (age) {}

void Student::eat (const string& food) {

cout << m_name << "," << m_age << "," << food

<< endl;

}

这是main.cpp文件:

#include "s.h"

// 使用Student类

int main (void) {

Student s ("张飞", 25);

s.eat ("包子");

return 0;

}

---------------------------------------------------

例子2:不同的构造函数

#include <iostream>

using namespace std;

class A {

public:

A (int a) {}

};

class Student {

private:

string m_name;

int m_age;

A m_a; //类类型的成员变量

public:

void eat (const string& food) {

cout << m_age << "岁的" << m_name

<< "同学正在吃" << food << "。" << endl;

}

//void _ZN7Student3eatERKSs (Student* this,

//const string& food) {

//cout << this->m_age << "岁的"<<this->m_name

//<< "同学正在吃" << food << "。" << endl;

//} //这是计算机中编译的样子

void setName (const string& name) {

if (name == "2")

cout << "你才" << name << "!" << endl;

else

m_name = name;

}

void setAge (int age) {

if (age < 0)

cout << "无效的年龄!" << endl;

else

m_age = age;

}

//如果同时有多种构造函数存在,则根据构造参数来确定调用哪个构造函数,既构造函数可以通过构造参数实现重载

// 构造函数

Student (const string& name, int age) :

m_a (100) {

m_name = name;

m_age = age;

}

// 无参构造

Student (void) : m_a (100) { //没有参数

m_name = "没名";

m_age = 0;

}

// 单参构造

Student (const string& name) : m_a (100),

m_name (name), m_age (0) {

m_name = "哈哈";

}

};

int main (void) {

Student s1 ("张飞", 25);

s1.eat ("包子");

//_ZN7Student3eatERKSs (&s1, "包子"); //编译器中的样子

Student s2 = Student ("赵云", 22);

s2.eat ("烧饼");

//_ZN7Student3eatERKSs (&s2, "烧饼");//编译器中的样子

Student s3; //调用的无参构造

s3.eat ("煎饼果子");

Student* s4= new Student ("关羽", 30);//调用有参构造,分配内存,并初始化

s4->eat ("烤鸭"); //当访问地址(指针或迭代器)的成员或数据时,用->

delete s4;

Student& s5 = *new Student ();//调用无参构造初始化

s5.eat ("面条"); //当访问直接对象的成员或数据时,用"."

delete &s5;

Student sa1[3] = {s1, s2}; //用s1与s2给数组初始化,但第三个元素没有赋值

sa1[0].eat ("KFC"); //25岁的张飞同学正在吃KFC

sa1[1].eat ("KFC"); //22岁的赵云同学正在吃KFC

sa1[2].eat ("KFC"); //0岁的无名同学正在吃KFC

Student* sa2 = new Student[3] {s1, s2}; // c++2011支持

sa2[0].eat ("KFC");

sa2[1].eat ("KFC");

sa2[2].eat ("KFC");

delete[] sa2;

Student s6 ("刘备");//调用单参构造

s6.eat ("米饭");//

return 0;

}

-----------------------------------------------

练习: 实现一个Clock类支持两种工作模式,计时器和电子钟。

00:01:00

14:05:37

#include <iomanip>

cout << setw(4) << setfill('0') << 12;

0012

Clock

时、分、秒

走 - 显示、滴答

练习答案:

#include <iostream>

#include <iomanip>

using namespace std;

class Clock {

public:

Clock (bool timer = true) :

m_hour (0), m_min (0), m_sec (0) {

if (! timer) {

time_t t = time (NULL);

tm* local = localtime (&t);

m_hour = local->tm_hour;

m_min = local->tm_min;

m_sec = local->tm_sec;

}

}

void run (void) {

for (;;) {

show ();

tick ();

}

}

private:

void show (void) {

cout << '\r' << setfill ('0')

<< setw (2) << m_hour << ':'

<< setw (2) << m_min << ':'

<< setw (2) << m_sec << flush;

//printf ("\r%02d:%02d:%02d", m_hour,

//m_min, m_sec);

}

void tick (void) {

sleep (1);

if (++m_sec == 60) {

m_sec = 0;

if (++m_min == 60) {

m_min = 0;

if (++m_hour == 24)

m_hour = 0;

}

}

}

int m_hour;

int m_min;

int m_sec;

};

int main (void) {

Clock clock (false);

clock.run ();

return 0;

}

-------------------------------------

五、this指针

1.一般而言,在类的构造函数或成员函数中,关键字this表示一个指针,对于构造函数而言,this指向正在被构造的对象,对于成员函数而言,this指向调用该函数的对象。

2.this指针的用途

1)在类的内部区分成员变量。

2)在成员函数中返回调用对象自身。

3)在成员函数内部通过参数向外界传递调用对象自身,以实现对象间交互。

老 -问题-> 学

师 <-答案- 生

class A {

B m_b;

};

class B {

A m_a;

};

sizeof(A) //error

class C {

C m_c;

};

例子1:

#include <iostream>

using namespace std;

class A {

public:

A (int data) : data (data) {

cout << "构造: " << this << endl;

//this->data = data;

}

void foo (void) {

cout << "foo: " << this << endl;

cout << this->data << endl;

}

int data;

};

int main (void) {

A a (1000); //创建对象调用了构造函数,并输出this的地址,输出"构造:0xbf9b24d8"

cout << "main: " << &a << endl;//输出该对象的地址,输出"main:0xbf9b24d8"

a.foo (); //该对象调用foo函数,输出this的值,以及输出this->data的值,输出"foo:0xbf9b24d8 1000"

A* pa = new A (1000); //创建对象调用构造函数,输出this的地址,输出"构造:0x95cc008"

cout << "main: " << pa << endl; //输出该对象的地址,输出"main:0x95cc008"

pa->foo (); //该对象调用foo函数,输出this以及this->data的值,输出"foo:0x95cc008 1000"

delete pa;

}

---------------------------------------------------

例子2:

#include <iostream>

using namespace std;

class Counter {

public:

Counter (void) : m_data (0) {}

Counter& inc (void) { //返回的是一个别名,不加&的话,返回的就是一个拷贝

++m_data;

return *this;

}

void print (void) {

cout << m_data << endl;

}

private:

int m_data;

};

int main (void) {

Counter c;

//c.inc ();

//c.inc ();

//c.inc ();

c.inc ().inc ().inc ();//函数返回的是一个别名,是一个左值,可以用来调用函数

c.print (); // 输出为3,如果前面的函数不加&,返回的只是拷贝,输出为1。

return 0;

}

例子3:学生与老师

#include <iostream>

using namespace std;

class Student; //因为在Teacher中会用到Student,所以提前声明一下

class Teacher {

public:

void educate (Student* s);//可以声明在类的内部,定义在类的外部

void reply (const string& answer) {

m_answer = answer;

}

private:

string m_answer;

};

class Student {

public:

void ask (const string& question, Teacher* t) {

cout << "问题:" << question << endl;

t->reply ("不知道。");

}

};

void Teacher::educate (Student* s) {

s->ask ("什么是this指针?", this);//将问题question和Teacher类变量的地址作为参数传递给Student类中的ask成员函数,并在ask函数中得到一个值作为参数传递给Teacher类中的replay函数,将值赋给m_answer,最后完成输出。

cout << "答案:" << m_answer << endl;

}

int main (void) {

Teacher t;

Student s;

t.educate (&s);

return 0;

}

---------------------------------------------

六、常函数与常对象

1.如果在一个类的成员函数的参数表后面加上const关键字,那么这个成员函数就被成为常函数,常函数的this指针是一个常指针。在常函数内部无法修改成员变量,除非该变量具有mutable属性。而且在常函数内部也无法调用非常函数。

2.常对象:拥有const属性的对象,兑现引用或指针。

常对象只能调用常函数。

同型的常函数和非常函数可以构成重载关系。常对象调用常版本,非常对象调用非常版本。如果没有非常版本,非常对象也可以调用常版本。

const XXX 函数名 (const YYY yyy)

const {

...

}

例子:

#include <iostream>

using namespace std;

class A {

public:

//void bar (void) { //函数1

//cout << "非常bar" << endl;

//}

//函数1与函数2构成函数重载

void bar (void) const { //函数2

cout << "常bar" << endl;

}

// void XXXbarYYY (A* this) {}

void foo (void) const { //函数3

//m_i = 100; //在常函数内部无法修改成员变量,除非那个成员变量有mutable属性,例:mutable int m_i;

const_cast<A*>(this)->m_i = 100;//也可以通过去常来解决

}

void print (void) const {

cout << m_i << endl;

}

//_ZNK1A3fooEv (const A* this) {

//const_cast<A*>(this)->m_i = 100;

//}

int m_i;

};

void func (void) /*const*/ {}

int main (void) {

A a;

a.foo (); //调用的是函数3

a.print ();//"100"

const A& r = a;//r为常对象,a为非常对象

r.bar ();//"常bar",r为常对象,常对象只能调用常函数

//XXXbarYYY (&r); // const A*

a.bar ();//"常bar",a为非常对象,可以调用常函数,也可调用非常函数

//XXXbarYYY (&a); // A*

return 0;

}

---------------------------------------------

七、析构函数

class 类名 {

~类名 (void) {

析构函数体;

}

};

当一个对象被销毁时自动执行析构函数。

局部对象离开作用域时被销毁,堆对象被delete时被销毁。

如果一个类没有定义任何析构函数,那么系统会提供一个缺省析构函数。缺省析构函数对基本类型的成员变量什么也不干,对类类型的成员变量,调用相应类型的析构函数。

一般情况下,在析构函数中释放各种动态分配的资源。

构造: 基类->成员->子类

析构: 子类->成员->基类

例子:

#include <iostream>

using namespace std;

class Double {

public:

Double (double data) :

m_data (new double (data)) {

cout << "构造" << endl;

} //构造函数早开始时执行,既创建对象时执行

~Double (void) {

cout << "析构" << endl;

delete m_data;

}//析构函数在对象结束时执行

void print (void) const {

cout << *m_data << endl;

}

private:

double* m_data;

string m_lable;

};

int main (void) {

//{

Double d1 (3.14);

d1.print ();//"构造,3.14"(d1调用的构造)

//}

Double* d2 = new Double (1.23);//"构造"(d2调用的构造)

delete d2; //"析构"(d2调用的析构)

cout << "再见!" << endl;//"再见"

return 0; //"析构",(程序结束,d1调用的析构)

}

-----------------------------------------------------------------------------------八、拷贝构造函数和拷贝赋值运算符

1.拷贝构造:用一个已有的对象,构造和它同类型的副本对象——克隆。

2.拷贝构造函数

形如

class X {

X (const X& that) { ... }

};

的构造函数成为拷贝构造函数。如果一个类没有定义拷贝构造函数,系统会提供一个缺省拷贝构造函数。缺省拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型的成员变量,调用相应类型的拷贝构造函数。

3.在某些情况就下,缺省拷贝构造函数只能实现浅拷贝,如果需要获得深拷贝的复制效果,就需要自己定义拷贝构造函数。

例子:拷贝函数

#include <iostream>

using namespace std;

class Integer {

public:

Integer (int data = 0) : m_data (data) {}//构造函数

void print (void) const {

cout << m_data << endl;

}

//拷贝构造(自己定义的):

Integer (const Integer& that) :

m_data (that.m_data) {

cout << "拷贝构造" << endl;

}

private:

int m_data;

};

void foo (Integer i) { //用Inerger类变量时实参给函数中Interger类的形参赋值,同样会调用拷贝构造函数

i.print ();

}

Integer bar (void) {

Integer i;

return i;

}

int main (void) {

Integer i1 (10);

i1.print ();//正常创建对象,输出"10"

Integer i2 (i1); // 调用拷贝构造,输出"拷贝构造"

i2.print (); //调用print函数,输出"10"

Integer i3 = i1; // 调用拷贝构造,输出"拷贝构造"

i3.print (); //调用print函数,输出"10"

//Integer i4 (10, 20);

cout << "调用foo()函数" << endl;

foo (i1); //调用拷贝构造函数,且调用print函数输出,所以输出为"拷贝构造 10"

cout << "调用bar()函数" << endl;

Integer i4 (bar ());

return 0;

}

---------------------------------------------------

4.拷贝赋值运算符函数

形如

class X {

X& operator= (const X& that) {

...

}

};

的成员函数称为拷贝赋值运算符函数。如果一个类没有定义拷贝赋值运算符函数,系统会提供一个缺省拷贝赋值运算符函数。缺省拷贝赋值运算符函数对于基本类型的成员变量,按字节复制,对于类类型的成员变量,调用相应类型的拷贝赋值运算符函数。

5.在某些情况就下,缺省拷贝赋值运算符函数只能实现浅拷贝,如果需要获得深拷贝的复制效果,就需要自己定义拷贝赋值运算符函数。

例子:拷贝赋值运算符函数

#include <iostream>

using namespace std;

class Integer {

public:

Integer (int data) : m_data (new int (data)) {}

//构造函数

~Integer (void) { //析构函数

if (m_data) {

delete m_data;

m_data = NULL;

}

}

void print (void) const {

cout << *m_data << endl;

}

Integer (const Integer& that) : //拷贝构造函数

m_data (new int (*that.m_data)) {}

void set (int data) {

*m_data = data;

}

//拷贝赋值运算符函数(运算符重载)

Integer& operator= (const Integer& that) {

// 防止自赋值

if (&that != this) {

// 释放旧资源

delete m_data;

// 分配新资源

m_data = new int (*that.m_data);

// 拷贝新数据

}

// 返回自引用

return *this;

}

private:

int* m_data;

};

int main (void) {

Integer i1 (10);

i1.print ();

Integer i2 (i1);

i2.print ();

i2.set (20);

i2.print ();

i1.print ();

Integer i3 (30);

i3.print (); // 30

i3 = i1; // 拷贝赋值

// i3.operator= (i1);

i3.print (); // 10

i3.set (40);

i3.print (); // 40

i1.print (); // 10

/*

int a = 10, b = 20, c = 30;

(a = b) = c;

cout << a << endl;

*/

(i3 = i1) = i2;

//i3.operator=(i1).operator=(i2);

i3.print ();

i3 = i3;

i3.print ();

return 0;

}

练习:实现一个简化版的字符串类String支持:

1.用字符指针形式的字符串构造

2.拷贝构造和拷贝赋值

3.获取字符指针形式字符串的成员函数,类似string::c_str

说明:不得使用std::string!

练习答案:

#include <iostream>

#include <cstring>

using namespace std;

class String {

public:

String (const char* str = NULL) { //构造函数

m_str = new char[strlen(str?str:"")+1];

strcpy (m_str, str ? str : "");

}

~String (void) { //析构函数

if (m_str) {

delete[] m_str;

m_str = NULL;

}

}

String (const String& that) : //拷贝构造函数

m_str (strcpy (

new char[strlen(that.m_str)+1],

that.m_str)) {}

/* 菜鸟

void operator= (const String& that) {

m_str = new char[strlen(that.m_str)+1];

strcpy (m_str, that.m_str);

}*/

String& operator= (const String& that) {

if (&that != this) {

/* 小鸟

delete[] m_str;

m_str = new char[strlen(that.m_str)+1];

strcpy (m_str, that.m_str);

*/

/* 大鸟

char* str =

new char[strlen(that.m_str)+1];

delete[] m_str;

m_str = strcpy (str, that.m_str);

*/

// 老鸟

String temp (that);

swap (m_str, temp.m_str);

}

return *this;

}

const char* c_str (void) const {

return m_str;

}

private:

char* m_str;

};

int main (void) {

String s1 ("Hello, World !");

cout << s1.c_str () << endl;

String s2 = s1;

cout << s2.c_str () << endl;

String s3 ("Hello, Linux !");

try {

s1 = s3;

}

catch (exception& ex) {

cout << ex.what () << endl;

}

cout << s1.c_str () << endl;

return 0; }

九、静态成员

1.静态成员变量和静态成员函数是属于类的而非属于对象。

2.静态成员变量,为多个对象所共享,只有一份实例,可以通过对象访问也可以通过类访问,必须在类的外部定义并初始化。静态成员变量本质上与全局变量并没有区别,只是多了类作用域的约束,和访控属性的限制。

class Account {

private:

string m_name;

double m_balance;

static double m_rate;

};

3.静态成员函数,没有this指针,无法访问非静态成员。

4.单例模式

例子0:

#include <iostream>

using namespace std;

class A {

public:

static int m_i;

static void foo (void) {

cout << "foo:" << m_i << endl;

//m_d = 3.14;//报错,静态成员函数不能访问非静态成员成员

//bar (); //报错,理由同上

}

double m_d;

void bar (void) {

m_i = 1000;//OK,非静态成员函数可以访问非静态成员,也可以访问静态成员

foo (); //OK,理由同上

}

};

int A::m_i = 1;//在外部定义

int main (void) {

A::m_i = 10;//通过类访问静态成员变量

A a1, a2;

cout << ++a1.m_i << endl;//通过对象访问静态成员变量,输出为"11"

cout << a2.m_i << endl; //因为静态成员变量,为多个对象共享,只有一个实例,所以上面a1将m_i修改为11,则通过a2访问的m_i也是11,输出为"11"

A::foo ();//输出为"foo:11",通过类访问静态成员函数

a1.foo ();//输出为"foo:11",通过对象访问静态成员函数

a1.bar ();//先调用bar,将m_i修改为1000,再调用foo,输出为"foo:1000"

return 0;

}

---------------------------------------------------

标签: #mfc office