Java程序设计课程学习总结与心得

时间:2024.3.20

Java程序设计课程学习总结与心得


第二篇:VC C++程序设计学习总结与心得


VC/C++程序设计学习笔记与心得

1、const的基本用法

1.1保护被限制的常量,如:

const int a = 3;

/*a = 4;——错误1*/

不能修改由const修饰的变量的值,且必须被初始化!

1.2便于进行类型检查,如:

const_test(const const_test & a)

{

property1 = a.property1;

property2 = a.property2;

/*a.property1 = 3;——错误2*/

//保护引用的参数,防止意外的修改

}

进行函数参数的保护和提醒

1.3为函数重载提供参考,如: void print()//print1 { cout << 1 << endl; } char print()const//print2 { cout << 2 << endl; return 'a'; } 常函数可以作为区分重载的标准 1.4节省空间,提高效率,避免不必要的内存分配,如: const定义的变量在内存中只保留一份拷贝,#define定义的常量却有若干份拷贝 1.5修饰指针,如: int b=1; const int * const a_ptr = &b;

b = 2;//正确 /* (*a_ptr) = 2;——错误4*/ //不能通过指针修改指向的值 /* a_ptr = &c;——错误5*/ //a_ptr = &b; 错 //定义的常量指针,不能修改指向的地址

第一个const限定不能由该指针去改变被指对象的值,第二个const限定不能改变指针所指的对象

1.6修饰引用,如: const int &bb = b; b = 2;//ok //bb = 2;//error

引用的对象默认就不可以修改,再加上第一个const之后,也不能通过本引用名对其进行修改

1.7修饰类的数据成员

必须在初始化列表中进行初始化或者在类外进行初始化,不能在构造函数的大括号里面进行初始化

2、面向对象与面向过程

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使

用的时候一个一个依次调用就可以了。

面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,

而是为了描叙某个事物在整个解决问题的步骤中的行为。

面向对象程序的基本特点:抽象、封装、继承、多态。

面向对象的优点:

1结构清晰。使人们的编程与实际的世界更加接近,所有的对象被赋予属性和方法,结果编程就更加富有人性化。

2封装性。减小外部对内部的影响。封装将对象有关的数据和行为封装成整体来处理,使得对象以外的部分不能随意存取对象的内部属性,从而有效地避免了外部错误对它的影响,大大减小了查错和排错的难度。

3 容易扩展,代码重用率高。容易扩展,在大框架不变的情况下很容易就开发出适合自己的功能,实现简单,可有效地减少程序的维护工作量,软件开发效率高。

面向过程的优点:易于经常以只读的方式读取数据

3、构造函数和析构函数

构造函数作用:在对象被创建时利用特定的值创建对象,将对象初始化一个固定的状态。 构造函数在创建时被自动调用。

编译器会自动生成无参数的构造函数,但是一旦你定义了自己的构造函数,系统默认构造函数将失效

拷贝构造函数作用:使用已经存在的一个对象,去初始化一个同类的新对象

拷贝构造函数调用的三种情况:

1、 类对象作为函数的返回值,执行完成返回时

2、 类对象作为函数的形参与实参结合时

3、 用类对象对另一个类对象进行赋值或初始化

析构函数作用:完成对象被删除前的清理工作,释放对象所占有的内存。

析构函数在对象生存期即将结束时被自动调用,且不接受任何参数

构造函数调用顺序:按声明的继承顺序,然后时定义类成员实例的顺序。类内嵌则先调用内嵌对象的构造函数,然后调用本类

析构函数调用顺序与其相反

深拷贝和浅拷贝:

若自动调用默认的拷贝构造函数是浅拷贝,与被拷贝对象共用内存

若想有独立内存,必须进行深拷贝,相当于利用被拷贝对象的成员变量值重新进行构造,如:

CmcArrayOfPoint2::CmcArrayOfPoint2(const CmcArrayOfPoint2 &array)

{

cout << "CmcArrayOfPoint2的拷贝构造函数被调用!" << endl;

this->m_n_num = array.m_n_num;

this->point_ptr = new CmcPoint[array.m_n_num];

for (int i=0; i<=this->m_n_num-1; i++)

this->get_point(i).set_xy(array.get_point(i).get_x(), array.get_point(i).get_y()); }

前向引用声明总结:

1、不能调用没有实现的类

2、可以以形参形式在内联函数中使用,但不能用于类内实现

3、不能在实现前涉及该类的任何细节

4、指针与引用

指针本身为unsigned long int类型

const在前为指向常量的指针,const在*后为指针类型的常量

不能声明void的类型的变量,但是可以声明使用void类型的指针,赋值时其他类型的指针可以赋值给void*,但是void指针对其他指针赋值需要进行强制类型转换。 函数指针:

数据类型 (*函数指针名)(形参表)【函数名本身就是函数指针】,如: void f1(int a);

void f2(int a,int b);

int f3(double a);

void f4(double a);

void (*func_ptr)(double);

double d = 1.1;

func_ptr = f4;

(*func_ptr)(d);

// func_ptr = f3; //错误!!!

// func_ptr = f2; //错误!!!

// func_ptr = f1; //错误!!!

必须使用时符合函数指针声明时的返回值类型和参数类型。

指向类成员函数的函数指针需要被声明为:

数据类型 (类名::*函数指针名)(形参表)如:

int (A::*fp)(void) = & A::get_a;

int (*f)(void);

//f = A::get_a; 错误!!

C++标准规定,不能将非静态成员函数名直接赋值给成员函数指针,必须进行取地址运算。

非静态成员函数调用时也必须基于类对象,利用 .* 运算符进行调用,如: cout << (Ca.*fp)() << ends;

对象指针是指指向对象的指针,this指针是隐含于每一个类的成员函数中的特殊指针(包括构造、析构函数),用于指向正在被操作的对象。

指向非静态数据成员的指针需要被声明为 类型 (类名::*指针名)

指向静态数据成员的指针需要被声明为 类型 *指针名

同理,非静态的数据成员也需要建立在类对象的基础上进行访问

5、静态数据成员和友元

静态数据成员具有静态生存期,可以用类名::标识符进行访问,由该类的所有对象共同维护和使用

必须在文件作用域对静态数据成员进行初始化,如:

int CmcPoint::c_n_count = 0;

//必须进行初始化定义性说明

友元函数是在类中定义的非成员函数(所以也不能引用this指针),如:

friend double get_length(const CmcPoint &p1,const CmcPoint &p2);

//返回两个点之间的距离(友元函数)

//不能用const修饰!????尽量传引用???

友元函数说明如下:

1)必须在类的说明中说明友元函数,说明时以关键字friend开头,后跟友元函数**的函数原型, 友元函数的说明可以出现在类的任何地方,包括在private和public部分;

2)注意友元函数不是类的成员函数,所以友元函数的实现和普通函数一样,在实现时不用"::"指示属于哪个类,只有成员函数才使用"::"作用域符号;

3)友元函数不能直接访问类的成员,只能访问对象成员,

4)友元函数可以访问对象的私有成员,但普通函数不行;

5)调用友元函数时,在实际参数中需要指出要访问的对象,

6)类与类之间的友元关系不能继承。

类的友元关系说明如下:

(1)友元关系不可继承

(2)友元关系是单向的

(3)友元关系是不可传递的

6、函数的重载和默认形参

定义:两个以上的函数,具有相同函数名和类似的功能,但是形参的个数和类型不同,编译器在调用时对其进行自动匹配。(C++语法,C语言不支持重载)

能作为重载的区分:

1、 函数的形参个数

2、 函数的形参类型

3、 常函数的const标识符

待默认形参的函数:声明时时只能从右向左缺省,而且必须给出缺省值

定义时(实现时)不必给出缺省值

7、运算符重载

运算符重载是指对已有的运算符赋予多重含义,使得同一个运算符作用与不同类型的数据时,导致不同的行为。可以以友元函数的形式或者类成员函数的形式。

不能重载的运算符:

类属运算符(.) 成员指针运算符(.*) 作用域运算符(::) sizeof运算符 和

三目运算符(?:)

关于运算符(++,--)前置后置问题,如

Clock& operator ++();//前置单目运算符重载

Clock& operator++(int);//后置单目运算符重载

调用时像正常形式调用就行

关于CString的运算符重载(声明部分):

class CMyString

{

public:

CMyString(int i_n_length = INIT_SIZE);//默认构造函数

CMyString(const char* i_c_str);//含有一个参数的构造函数

CMyString(const CMyString &i_s_str);//拷贝构造函数

~CMyString();//析构函数

CMyString operator=(const CMyString i_s_str);//重载赋值运算符

CMyString operator+(const CMyString i_s_str);//重载加法运算符

CMyString operator+=(const CMyString i_s_str);//重载加等于运算符

friend bool operator==(const CMyString &i_s_str1, const CMyString &i_s_str2); //重载判断是否相等运算符

void show()const;//打印字符串

private:

char* m_c_path;//初始地址

int m_n_length;//当前字符串长度

int m_n_max_size;//最大的储存空间

};

实现部分(基于<string.h>):

CMyString::CMyString(int i_n_length/*INIT_SIZE*/)//默认构造函数

{

this->m_c_path = (char*)malloc(sizeof(char) * i_n_length);

this->m_c_path[0] = '\0';

m_n_length = 0;

m_n_max_size = i_n_length;

}

CMyString::CMyString(const char* i_c_str)//含有一个参数的构造函数

{

this->m_n_length = strlen(i_c_str);

this->m_n_max_size = strlen(i_c_str);

this->m_c_path = (char*)malloc(sizeof(char) * this->m_n_length);

strcpy(this->m_c_path,i_c_str);

}

CMyString::CMyString(const CMyString &i_s_str)//拷贝构造函数

{ this->m_c_path = (char*)malloc(sizeof(char) * i_s_str.m_n_max_size); for (int i=0; i<=i_s_str.m_n_length/*!!!不是-1!!!*/; i++) this->m_c_path[i] = i_s_str.m_c_path[i]; this->m_n_length = i_s_str.m_n_length; this->m_n_max_size = i_s_str.m_n_max_size; } CMyString::~CMyString() { this->m_c_path = NULL;

this->m_n_length = 0;

this->m_n_max_size = 0;

}

CMyString CMyString::operator=(const CMyString i_s_str)

{

free(this->m_c_path);

this->m_c_path = (char*)malloc(sizeof(char) * i_s_str.m_n_max_size); this->m_n_length = i_s_str.m_n_length;

this->m_n_max_size = i_s_str.m_n_max_size;

for (int i=0; i<=i_s_str.m_n_length/*!!!不是-1!!!*/; i++)

this->m_c_path[i] = i_s_str.m_c_path[i];

return i_s_str;

}

CMyString CMyString::operator+(const CMyString i_s_str)

{

CMyString c_str(this->m_n_max_size + i_s_str.m_n_max_size);

c_str.m_n_max_size = this->m_n_max_size + i_s_str.m_n_max_size; c_str.m_n_length = this->m_n_length + i_s_str.m_n_length;

c_str.m_c_path = (char*)malloc(sizeof(char) * c_str.m_n_max_size);

strcpy(c_str.m_c_path, this->m_c_path);

strcat(c_str.m_c_path, i_s_str.m_c_path);

c_str.m_c_path[c_str.m_n_length] = '\0'; return c_str; } CMyString CMyString::operator+=(const CMyString i_s_str) { this->m_n_max_size += i_s_str.m_n_max_size; this->m_c_path = (char*)realloc(this->m_c_path, sizeof(char) * this->m_n_max_size); this->m_c_path[this->m_n_length] = '\0'; this->m_n_length += i_s_str.m_n_length; strcat(this->m_c_path, i_s_str.m_c_path);//需要足够的空间 return (*this); } void CMyString::show()const//打印字符串 { cout << this->m_c_path << endl; } bool operator==(const CMyString &i_s_str1, const CMyString &i_s_str2) //重载判断是否相等运算符 { if (i_s_str1.m_c_path==NULL || i_s_str2.m_c_path==NULL) { if (i_s_str1.m_c_path != i_s_str2.m_c_path)//不同时为空 return false; } if (i_s_str1.m_n_length != i_s_str2.m_n_length) return false; if (strcmp(i_s_str1.m_c_path, i_s_str2.m_c_path)!=0) return false; return true; } 微软的实现方式: char* strcpy(char * dst, const char * src)

{ char * cp = dst; while( *cp++ = *src++ ) ; // Copy src over dst return( dst ); }//Microsoft 的实现方式 int strcmp ( const char* src, const char* dst ) { int ret = 0 ; while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) ++src, ++dst; if ( ret < 0 ) ret = -1 ; else if ( ret > 0 ) ret = 1 ;

return( ret ); }//Microsoft 的实现方式 char* strcat ( char * dst , const char * src ) { char * cp = dst; while( *cp )

cp++; // find end of dst

while( *cp++ = *src++ ) ; // Copy src to end of dst

return( dst ); // return dst

}//Microsoft 的实现方式

8、类的继承和派生

构造函数和析构函数不会被继承

私有继承和protected类型继承会把父类的public成员变成对应的类型,有多个基类(多继承),一个(单继承)

从两个不同基类继承来的同名成员,如果不在本类进行覆盖,直接调用,将会导致二义性,编译无法通过,需要利用作用域运算符,会有两份内存分配。默认为私有继承。 公有继承:public和protected类属性不变,private类被隐藏,不能直接进行调用。

保护继承:public和protected类以protected类的属性出现,私有成员不能直接访问。 私有继承:public和protected类以private类的属性出现,私有成员不能直接访问。

构造派生类对象时,就要对基类数据成员、新增数据成员和成员对象的数据成员进行初始化。

9、虚基类、虚函数、纯虚函数

将继承中的共同基类设置成虚基类,这时从不同路径继承来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射,不会出现二义性。

如:

class CmcBird : virtual public CmcAnimal

//将CmcAnimal视为虚基类

class CmcHorse : virtual public CmcAnimal

//将CmcAnimal视为虚基类

class CmcFlyHorse : public CmcBird, public CmcHorse

如果使用非默认形式的构造函数,在整个继承关系中,直接或者间接继承虚基类的所有派生类,都必须在构造函数初始化列表中给出初始化。

虚函数是动态绑定的基础,且必须是非静态的成员函数。(如果认为该类会被继承,最好声明其方法为虚函数,但是需要生成虚函数列表,会一定程度上影响性能)

通俗来看,如果一个基类成员函数不是虚函数的话,其派生类将该成员函数覆盖后,利用该基类的指针(或引用),即使指向(引用)的是一个派生类的实例,也只能对基类的函数进行调用。如:

CmcAnimal *animal = new CmcAnimal;

CmcAnimal *bird = new CmcBird;

CmcAnimal *horse = new CmcHorse;

animal->move();

horse->move();

bird->move();

其中CmcBird、CmcHorse类分别继承了CmcAnimal类,并覆盖了其move()方法,如果在CmcAnimal类中,move()不是虚函数,运行结果为:

VCC程序设计学习总结与心得

如果用virtual标识符对CmcAnimal的move函数进行声明(定义时不必加),即: virtual void move();//动物的移动函数 运行结果为:

VCC程序设计学习总结与心得

即基类指针分别正确调用了子类的move()方法

注意:不能声明类的虚构造函数,析构函数可以,用来指向性清理

抽象类是为了抽象和设计为目的建立的,不能实例化其本身,只能实例其非抽象派生类。 抽象类带有纯虚函数。

纯虚函数是特殊的虚函数(比虚函数还虚……),声明举例如下:

virtual void move() = 0;//动物的移动函数,纯虚函数,必须进行覆盖

声明了纯虚函数后,基类中就不必给出其实现部分【试了一下,给出实现部分也能编译通过,但是没有任何实际意义,因为子类肯定会覆盖它】,函数体由派生类给出。 因为你无法使用基类指针指向基类,如:

//CmcAnimal *animal = new CmcAnimal;

//error因为包含纯虚函数,因此CmcAnimal是一个抽象类

所以无法通过指针调用纯虚函数在基类中的实现,因此完全没有意义。

但是你可以声明抽象基类的指针指向非抽象子类,这是正确的,而且能调用正确的方法: CmcAnimal *bird = new CmcBird;

CmcAnimal *horse = new CmcHorse;

但是但是,如果通过这种方法:

CmcHorse horse2;

horse2.CmcAnimal::move();

//ok!可以利用作用域进行直接调用,虽然是纯虚函数,但是可以被调用!

//不过既然作为纯虚函数,有定义应该是件不适合的事情

强行通过子类实例的作用域运算符对其调用,可以使用该纯虚函数!!!——非常有趣

VCC程序设计学习总结与心得

这样做未免有点过于较劲了,而忽略了纯虚函数的抽象类的目的,只有你绝对肯定不需要使用该类的对象和其成员方法时,你才会将其声明为抽象类和纯虚函数。

最后,如果一个类由抽象类派生,但没有完全重写改抽象类的所有纯虚函数,那它仍然是一个抽象类。

10、模板函数与模板类

利用模板可以将处理的对象的类型参数化,使得一段程序可以用于处理多种不同类型的对象。定义形式为:

template <typename T>

inline const T& max(const T &i_t_a, const T &i_t_b, const T &i_t_c)

{

return max(max(i_t_a, i_t_b), i_t_c);

}

或者:

template <class T>

inline const T& max(const T &i_t_a, const T &i_t_b)

{

return i_t_a > i_t_b ? i_t_a : i_t_b;

}

可以声明多参数:

template <typename T1, typename T2>

void print(const T1 &t1, const T2 &t2)

{

cout << t1 << endl << t2 << endl;

}

print("I love Coding C++", 12.08);

运行结果为:

VCC程序设计学习总结与心得

模板类:

使用类模板课以为类声明一种模式,使得类的一些数据成员、函数成员的参数、返回值可以取任意类型。如:

template <class T, class T2>

class CmcTemplate

{

public:

CmcTemplate();//默认构造函数

CmcTemplate(T i_t_elem);//初始化类数据的构造函数

CmcTemplate(const CmcTemplate &i_c_t);//拷贝构造函数

~CmcTemplate();

T get_elem();//得到类的成员变量

void set_elem(T i_t_elem);//设置类的成员变量

T2 test_t2;//测试两模板的模板类

private:

T m_t_elem;//类的数据成员

bool is_initial;//已经初始化类成员为true,否则为false

};

写类的函数实现时要按照如下形式:

template <class T, class T2>

void CmcTemplate<T,T2>::set_elem(T i_t_elem)

//设置类的成员变量

{

m_t_elem = i_t_elem;

is_initial = true;

}

实例化时:

CmcTemplate<int,int> a(12);//用int型成员变量

关于模板类实现部分和声明部分分开两个文件,需要在包含.h文件时,同时包含.cpp实现文件,因为编译时,需要给模板类生成具体的代码。

注:关于模版类成员函数声明和实现的分离问题,

可参照资料出处:

11、字符串的操作

C++常用于字符串操作的有3种数据类型。

11.1 char*

可用方法集中在<string.h>中,除去strcpy(),strcmp(), strlen(), strcat()这些常用简单方法,还有一些实用方法,如:

char* strdup(const char*);//相当于赋值语句

char* strpbrk(const char* s1, const char* s2);//得到第一个在s2出现,也在s2中出

//现的位置指针,没有返回NULL

int strcspn(const char* s1, const char* s2);//得到strpbrk()中的下标

char* strchr(const char*, char);//找字符串中char的位置,不存在返回NULL char* strrchr(const char*, char);//从后向前找字符

char* strstr(const char*s1, const char* s2);//找到s2在s1中的位置

char* strrev(char*);//将字符串倒置

char* strnset(char*, char, int n);//用n个char替代char*

char* strupr(char*);//所有的小写字母变成大写

char* strlwr(char*);//所有的大写字母变小写

char* strerror(int);//返回对应错误值的字符串

void* memcpy(void*, const void*, int);//对缓冲区进行拷贝

11.2 string

这是一个异常强大的类,在C++数据处理中处处发挥着重要的作用,尤其是重载的运算符较于<string.h>的使用,有了很大的方便性,这里也列举出一些实用的方法。

int length();//返回长度

int find_first_of(const string &s, int pos = 0);

//从pos开始查找当前串中第一个在串s出现的字符下标,失败返回string::npos int find_last_of(const string &s, int pos = 0);

//同理,只是从后向前找

string &assign(const string &s, int start, int n);

//将s由start开始的n个字符赋值给当前串

[],at()//均可用于获取字符,at()会在越界时抛出out_of_range异常

string &replace(int p0, int n0, const string &s);

//实用s替代本串中由p0开始的n0个字符 string &insert(int p0, int n, char c); //在本串p0处插入n个c string &insert(int p0, const string &s); //在本串p0处插入字符串s int find(****, int pos=0); //从pos开始找对应东西的位置 注意:不能对lenght()以外的区域赋值,需用insert()

11.3 CString 这是MFC封装的一个类,基于char*,但是同时封装了很多实用的方法,重载了很多运算符。参照<AFX.H>,可得 // get data length int GetLength() const; // TRUE if zero length BOOL IsEmpty() const; // clear contents to empty void Empty(); // return single character at zero-based index TCHAR GetAt(int nIndex) const; // return single character at zero-based index TCHAR operator[](int nIndex) const; // set a single character at zero-based index /*以下资料取自/lijuwen/article/details/623186*/

int Compare( LPCTSTR lpsz ) const;

返回值 字符串一样 返回0

小于lpsz 返回-1

大于lpsz 返回1

区分大小字符

int CompareNoCase( LPCTSTR lpsz ) const;

返回值 字符串一样 返回0

小于lpsz 返回-1

大于lpsz 返回1

不区分大小字符

int Delete( int nIndex, int nCount = 1);

返回值是被删除前的字符串的长度

nIndex是第一个被删除的字符,nCount是一次删除几个字符。根据我实验得出的结果:当nCount>要删除字符串的最大长度(GetCount() - nIndex)时会出错,当nCount过大,没有足够的字符删除时,此函数不执行。

int Find( TCHAR ch ) const;

int Find( LPCTSTR lpszSub ) const;

int Find( TCHAR ch, int nStart ) const;

int Find( LPCTSTR lpszSub, int nStart ) const;

返回值 不匹配的话返回 -1; 索引以0 开始

nStar 代表以索引值nStart 的字符开始搜索 ,

即为包含以索引nStart字符后的字符串

void Format( LPCTSTR lpszFormat, ... );

void Format( UINT nFormatID, ... );

lpszFormat 一个格式控制字符串

nFormatID 字符串标识符

LPTSTR GetBuffer( int nMinBufLength );

返回值

一个指向对象的(以空字符结尾的)字符缓冲区的LPTSTR 指针。

参数

nMinBufLength

字符缓冲区的以字符数表示的最小容量。这个值不包括一个结尾的空字符的空间。

int Insert( int nIndex, TCHAR ch );

int Insert( int nIndex, LPCTSTR pstr );

返回修改后的长度,nIndex是字符(或字符串)插入后的索引号例子

CString Left( int nCount ) const;

throw( CMemoryException );

返回的字符串是前nCount个字符。

void MakeLower( );

改变字符的小写

void MakeReverse( );

字符倒置

void MakeUpper( );

改变字符的大写 CString Mid( int nFirst ) const; CString Mid( int nFirst, int nCount ) const;

nCount代表要提取的字符数, nFirst代表要提取的开始索引位置、

int CString::Remove ( TCHAR ch );

返回值

返回从字符串中移走的字符数。如果字符串没有改变则返回零。

参数

ch

要从一个字符串中移走的字符。

说明

此成员函数用来将ch 实例从字符串中移走。与这个字符的比较是区分大小写 的。

int Replace( TCHAR chOld, TCHAR chNew );

int Replace( LPCTSTR lpszOld, LPCTSTR lpszNew );

返回值

返回被替换的字符数。如果这个字符串没有改变则返回零。

参数

chOld

要被chNew 替换的字符。

chNew

要用来替换chOld 的字符。

lpszOld

一个指向字符串的指针,该字符串包含了要被lpszNew 替换的字符。

lpszNew

一个指向字符串的指针,该字符串包含了要用来替换lpszOld 的字符。 说明

此成员函数用一个字符替换另一个字符。函数的第一个原形在字符串中用chNew 现场替换chOld。函数的第二个原形用lpszNew 指定的字符串替换lpszOld 指定 的子串。

在替换之后,该字符串有可能增长或缩短;那是因为lpszNew 和lpszOld 的长度 不需要是相等的。两种版本形式都进行区分大小写的匹配。

int ReverseFind( TCHAR ch ) const;

返回值

返回此CString 对象中与要求的字符匹配的最后一个字符的索引;如果没有找 到需要的字符则返回-1。

参数

ch

要搜索的字符。

说明

此成员函数在此CString 对象中搜索与一个子串匹配的最后一个字符。此函数 类似于运行时函数strrchr。

CString Right( int nCount ) const;

throw( CMemoryException );

返回的字符串是最后nCount个字符。

12、windows程序执行原理

在程序开始后,要先初始化应用程序,然后创建应用程序窗口。【初始化完毕】 然后进入消息循环,等待并获取消息

消息传入途径有两种,一种是用户输入消息,另一种为系统消息。

然后得到消息要判断是否为退出程序(WM_QUIT),是的话就结束程序

否则判断是否为本应用程序应该处理的信息

是,则调用对应的消息处理函数进行处理,然后重新进入消息循环获取消息 否,则调用默认处理函数,然后重新进入消息循环获取消息

如飞信客户端登陆程序,首先运行程序后进行初始化,然后创建可视化窗口。 这时进入消息队列,获取用户和系统的信息。

此时给它一个登录的消息,首先程序确定不是WM_QUIT,然后判断是否需要进行处理,当然需要本应用程序处理。于是它调用登录时对应的消息处理函数,完成对应的功能后,重新进入消息队列。这时点击退出,即发送了WM_QUIT信息,则其获取后,判断需要退出,然后释放内存占用,结束程序。

13、MFC程序的执行过程

与所有的windows应用程序一样,MFC应用程序也有作为程序入口点的WinMain()主函数,对应WinMain.cpp中的函数AfxWinMain()

MFC程序启动时,首先创建theApp对象,然后调用应用程序类对象进行初始化,然后调用AfxWinMain()主函数。在该函数中,首先调用全局函数AfxGetApp()来获取theApp的指针pApp,然后调用theApp的成员函数InitInstance()来初始化程序。在初始化过程中,还同时生成了文档(doc),视图(view),主框架窗口(frame),并生成工具栏和状态栏。在初始化最后,应用程序将收到WM_PAINT消息,框架会自动调用视图类的OnDraw()函数绘制程序客户区窗口。

InitInstance()执行完毕后,AfxWinMain()将调用成员函数Run()进入消息循环队列,直到收到WM_QUIT消息。若队列中无消息,Run()将调用函数OnIdle()进行空闲时的处理。MFC首先调用CWinApp类的成员函数ExitInstance(),然后调用静态对象的析构函数,最后退出程序,交还控制权给操作系统。

14、消息映射和消息处理

windows消息有三种类型:

14.1 标准windows信息

14.1.1 键盘信息

键盘信息与键的动作相关联,常见信息:

WM_KEYDOWN, WM_KEYUP,

WM_CHAR(输入非系统字符时产生的信息)

14.1.2 鼠标消息

WM_MOUSEMOVE, WM_RBUTTONDOWN, WM_LBUTTONDOWN,

WM_LBUTTONDBLCLICK

14.1.3 窗口信息

WM_PAINT 当客户区产生移动或者显示事件,当用户窗户产生改变大小事

件、当产生下拉菜单关闭并需要回复被恢复部分事件、当产生windows清除

对话框或者消息框并需要恢复被覆盖部分事件时。

处理函数原型为:

afx_msg void OnPaint();

WM_TIMER 每当计时器触发时。

处理函数原型为:

afx_msg void OnTimer(UINT uIDEvent);//参数为计时器标示符

14.2 控件信息

指控件或其他子窗口向父窗口发送的WM_COMMAND消息,如图:

VCC程序设计学习总结与心得

14.3 命令消息

由菜单项、工具栏按钮、快捷键等用户界面对象发出的WM_COMMAND消息。

与MFC映射机制有关的宏有:DECLARE_MESSAGE_MAP()

BENGIN_MESSAGE_CLASS(MyClass, MyBaseClass)

END_MESSAGE_MAP()

获取空间信息时可以使用,GetDlgItemText(ID标识符, 缓冲区地址)

但是获取时是以CString的形式获取的。同样写出变量时用SetDlgItemText(ID标识符, 缓冲区地址)。仅限于本类获取。

还有一种较为方便的方法,就是给空间设置监听变量,如图:

VCC程序设计学习总结与心得

这时,只需调用UpdateData(bool);即可完成数据的传递,此函数的缺省参数值为true,表示从控件向变量里面读取数据。如果函数的参数是false,则将变量中的数据写到控件中。

15、文档视图结构

(1)在View中获取Doc指针

C**Doc *pDoc = GetDocument();

//一个viw仅有一个Doc指针

(2)在App中获取MainFrame指针

直接使用成员变量m_pMainWnd

(3)在View中获取MainFrame指针

CMainFrame *pMain = (CMainFrame*)AfxGetApp()->m_pMainWnd;

(4)获得已经建立的View指针

CMainFrame *pMain = (CMainFrame*)AfxGetApp()->m_pMainWnd;

C**View *pView = (C**View)pMain->GetActiveView();

15、对话框部分基本知识

建立一个对话框,初始会有CAboutDlg(帮助信息),App和Dlg三个类 可以自己新建对话框和对话框类。对话框有两种:

一种是模态对话框,当显示时程序会暂停执行,直到关闭此模态对话框

构造时使用方法DoModal(),退出时需要调用EndDialog(0);

VCC程序设计学习总结与心得

另一种是非模态对话框,当显示时允许执行其他任务(多线程)。 构造时调用方法 Create(ID, this); 和 ShowWindow(int) 其中定义的常用窗口常量值为: #define SW_HIDE 0 #define SW_SHOWNORMAL 1 #define SW_NORMAL 1 #define SW_SHOWMINIMIZED 2 #define SW_SHOWMAXIMIZED 3 #define SW_MAXIMIZE 3 #define SW_SHOW 5 #define SW_MINIMIZE 6 #define SW_SHOWDEFAULT 10 退出时调用EndDialog(); 处理对话框接受回车退出,或者接受ESC键退出,可在对话框类中添加虚函数 BOOL PreTranslateMessage(MSG* pMsg)屏蔽这些键盘信息。如: BOOL CToLWYDlg::PreTranslateMessage(MSG* pMsg)

{

// TODO: Add your specialized code here and/or call the base class

if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) return true;

if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) return true;

if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_SPACE) return true;

return CDialog::PreTranslateMessage(pMsg);

}

其中的参数MSG*<WINUSER.H>为:

typedef struct tagMSG {

HWND hwnd;

UINT message;

WPARAM wParam;

LPARAM lParam;

DWORD time;

POINT pt;

#ifdef _MAC

DWORD lPrivate;

#endif

} MSG;

可以在App的InitInstance()中使用函数SetDialogBkColor(COLORREF, COLORREF);改变对

话框的默认背景颜色,如:

SetDialogBkColor(RGB(150, 246, 244), RGB(0, 0, 0));

RGB是一个宏,定义如下:

#define RGB(r,g,b)

((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16))) COLORREF也不是一个类:typedef DWORD COLORREF;//是一个无符号长整型

注:在子对话框中建立保存父对话框的指针变量,有利于操作。可以在子对话框初始化时获取父对话框的指针变量。

16、动态链接库DLL

从MFC导出函数常用以下两种方法:利用.def文件和关键字__declspec(dllexport) 在对应的dll.def文件中进行导出函数接口定义,在.h中添加函数原型: extern "C" int devide(int a, int b, int &nResult); 在.cpp文件对应进行实现: extern "C" devide(int a, int b, int &nResult); { //do something } 或者用关键字__declspec(dllexport),在.h中添加声明: extern "C" __declspec(dllexport) int devide(int a, int b, int &nResult); 在.cpp中添加实现: extern "C" __declspec(dllexport) int devide(int a, int b, int &nResult) { //do something } 如果要进行类的导出,可以按如下进行声明: extern "C" class __declspec(dllexport)CmcCalculator

{

//has something

};

这时进行编译、链接,会生成对应的.lib和.dll文件

使用DLL时,可以使用隐式链接,如:

#pragma comment (lib, "CmcCalculatorDll.lib")

或者进行工程设置

使用时语法为:class __declspec(dllimport)CmcCalculator{} ;

先要进行函数或者类的声明,然后才能对其使用。

17、多线程、进程

进程是应用程序的执行实例,是操作系统分配资源单位。

线程是操作系统分配处理器的最基本单元,它是进程内部的一个独立的执行单元。 程序运行时,由系统自动创建一个进程,自动执行该进程的主线程,主线程终止,进程也就终止。

用户可以在需要时创建该进程的其它线程,多线程可以并发地运行在一个进程里面,线程之间的通信非常便利。

多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。 线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。

多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。 多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线程调度,同步等问题。

简单来说:如果一个线程无法进行【如遇到了Sleep()】,比如线程要求的键盘输入尚未获得,则转入另一线程的工作。通常,CPU在线程间的切换非常迅速,使人们感到好像所有的线程是同时进行的。多线程编程环境用方便的模型隐藏CPU在任务间切换的事实。

17.1 创建工作者线程

线程函数的格式是固定的,如:UINT work_thread1(LPVOID pParam);

需要定义一个线程函数

关键字:volatile

//用来修饰被不同线程访问修改的变量

17.2 创建用户界面线程

首先要从CWinThread派生一个新类,并重写派生类的InitInstance()、ExitInstance()、及Run()等函数,然后使用AfxBeginThread()启动用户界面线程。

调用CWinThread类自带的AfxBeginThread() ResumeThread()SuspendThread()

GetThreadPriority() SetThreadPriority(),分别进行进行启动线程、挂起、唤醒、设置优先级操作,利用改变全局变量的值进行终止进程操作

18、C++个人学习心得:

我从来没有想过一个学期的课程能让自己收获那么多,首先真心感谢马老师那么认真负责地传授给我们实用的C++/VC知识。

想想当时同时选修C++和JAVA的目的很单纯:首先自己比较喜欢编程,喜欢小C,又痴迷于编写小游戏。在大一上学期的程序设计课里,用egg库进行

多线程贴图,总是感觉很低效,于是寄希望于C++的可视化编程。同时也很想努力C++和JAVA的学习,在总评上提高平均成绩。

先谈谈自己对这门课的理解和看法吧:我非常喜欢上机的这种教学形式,能让课程不仅仅是学生被动的学习,能真正每堂课都参与到实际coding中,很棒!而且每节课都能上机,课堂效率也很高,很给力!但是这门课带给我的压力还是蛮大的,真的过程很煎熬、很辛苦!我自认为小C的基础比较扎实,实际编程中Debug能力比较强,但是面对作业还真是无数次都是熬到凌晨2、3点钟,一直到电脑没电。有时候作业量确实比较大,甚至如果那一部分的基础不扎实的话,是很难完成的。不过,我一直坚持用心去敲代码,绝不粘别人的程序。这样一学期下来,竟然能放下结构化编程的思想,慢慢转化成面向对象的C++编程思想,甚至还学习了那么多实用的VC知识,真的发现自己的编程水平也有不小的进步,谢谢马老师。

要是得总结一些学习经验,那还是要多敲代码,平时多尝试一些Bug,有Bug才有收获。还有,对于一些知识点有疑问,可以到网上去自学。在遇到一些自己解决不了的Bug或者实现不了的功能(如:子对话框关闭父对话框、MFC各种指针的获取、多线程和进程、模板类代码生成),在各种论坛和大牛们的博客里面都能收获很多。

最后就是保持热情和坚持,平静面对失败,坚守原则吧。 再次向马老师的认真、热情、负责表示感谢,遇见您真好。

更多相关推荐:
课程设计心得体会

心得体会通过这次课程设计,让我更加深刻了解课本知识,和以往对知识的疏忽得以补充,在设计过程中遇到一些模糊的公式和专业用语,比如说经济刮板运输机及皮带运输的选择,在选择选择刮板皮带运输选型时,在使用手册时,有的数…

课程设计学习心得

课程设计学习心得思考累积智慧,实践创造财富!经历过这两周的课程设计,我们有所得有所获。我们不但在设计过程中逐步养成了发现、提出、分析和解决实际问题的习惯,而且锻炼提高了自己的实践能力。对于此次电子时钟的设计,让…

万能课程设计心得体会2个

1两周的课程设计结束了,在这次的课程设计中不仅检验了我所学习的知识,也培养了我如何去把握一件事情,如何去做一件事情,又如何完成一件事情。在设计过程中,与同学分工设计,和同学们相互探讨,相互学习,相互监督。学会了…

课程设计心得体会(通用)

课程设计心得体会通用通过此次课程设计使我更加扎实的掌握了有关方面的知识在设计过程中虽然遇到了一些问题但经过一次又一次的思考一遍又一遍的检查终于找出了原因所在也暴露出了前期我在这方面的知识欠缺和经验不足实践出真知...

课程设计的心得体会

心得体会两周的课程设计结束了,通过这次课程设计,我更加扎实的掌握了振荡电路,放大电路,整形电路和滤波电路,检测电路方面的知识,在设计的过程中也遇到了不少的问题,不过经过一遍遍的思考以及和老师同学们的讨论都一一得…

计算机网络课程设计心得体会

课程设计是每一个大学生在大学生涯中都不可或缺的,它使我们在实践中了巩固了所学的知识、在实践中锻炼自己的动手能力;实习又是对每一位大学生所学专业知识的一种拓展手段,它让我们学到了很多在课堂上根本就学不到的知识,不…

课程设计心得体会

心得体会两个星期的课程设计终于完了,此次的课程设计让我感触很多,不仅仅是知识上的学习和掌握,同时也让我明白了很多做人的道理。在开始阶段,老师让我们了解一些基本知识,当自己照着学习指导上的内容完成了一个课题时那种…

课程设计报告及心得体会15

管理信息系统课程设计报告个人大报告班级学号姓名指导老师秦胜君日期20xx年5月10日评阅成绩教师签名在这次设计过程中我主要负责第一阶段系统分析阶段对系统需求和可行性分析第二阶段数据流程图第四阶段编制系统使用说明...

高中语文新课程创新设计培训心得体会

高中语文新课程创新设计培训心得体会经过近一个月的对高中语文新课程创新设计的网络培训学习我学到如下东西我们都应该知道高中语文课程标准是从工具性与人文性的统一角度出发以语文能力培养为核心将语文课程目标有机整合为积累...

机械课程设计心得体会

机械课程设计心得体会范文一经过一个月的努力我终于将机械设计课程设计做完了在这次作业过程中我遇到了许多困难一遍又一遍的计算一次又一次的设计方案修改这都暴露出了前期我在这方面的知识欠缺和经验不足刚开始在机构设计时由...

《课程设计与评价》学习心得

课程设计与评价心得体会金马镇石缸冲小学李媛媛通过此次课程设计与评价的学习使我更加扎实的掌握了有关课程设计与评价方面的知识在学习过程中虽然遇到了一些问题但经过一次又一次的思考终于找出了原因所在也暴露出了之前我在这...

电子课程设计实习报告

河南科技学院机电学院电子课程设计报告题目声光控制器设计专业班级应用电子技术教育111姓名张胜林20xx0325117时间20xx12920xx1227指导教师张伟邵锋完成日期20xx年12月20日声光控制器设计...

课程设计学习心得(46篇)