奔跑的小河
Talk is cheap. Show me the code.

导航

 

作为一个C++菜鸟,我将我这半年学习C++的一点小心得分享出来。这些都是零散的一些,一条条地看就可以。

1.C++/CLI是ISO/ANSI C++的扩展。同时visual C++可以开发这两种不同的C++程序。

C++/CLI:扩充版本定义的,在CLR中运行的程序。又叫做CLR程序。
ISO/ANSI C++:使用ISO/ANSI标准定义的。又叫做,本地C++程序
CLI Common Language Infrastructure 通用语言基础结构
CLR Common Language Runtime 公共语言运行时

2.CLI是一种标准规范,CLR是微软公司在CLI规范基础上实现的。

     在勾选了默认选项后会出现预编译头文件stdafx.h 文件。这种机制是为了当程序中有大量的文件时使编译过程效率更高。

    一般我们自己的类中都要包含这个.h头文件,同时我们使用到的头文件也要包含在其中。
3.静态变量的默认初始值始终是0;而自动变量默认初始化的值是它们所占用内存的程序留下来的值。

4.

拷贝构造函数是复制一个对象到新的对象。
    赋值操作符是将一个对象赋值到另外一个已经存在的对象。
  注:拷贝构造函数一用完就会调用调用析构,而赋值操作符是将对象的值复制到另一个对象中所以不存在创建。

5. 多态中。派生类的指针不能指向基类!!
  多态就是指针指向哪个对象就调用哪个对象的成员函数。
6.
#pragma once
表示这个文件只被包含一次。
而不是下面的#include<>只包含 一次。

-------------------------------------------------------------------------------------------------------------------
1.内联函数
函数声明时声明inline或者不声明直接在函数实现出加inline
在类中的函数声明处实现的函数也属于内联函数。

缺点:如果调用多次,就会增加代码在内存中的体积(程序体积)。
优点:免去多次在函数与调用之间跳转,提高运行效率。

所以,一般采用折衷的方法,当函数代码比较少时,就用内联函数。
 
2.#pragma once 这个宏的含义 (pragma 编译,注释)
   为避免同一个文件被include多次,C/C++中有两种宏实现方式:一种是#ifndef方式,一种是#pragma once方式。
在能够支持这两种方式的编译器上,二者并没有太大的区别,但两者仍然有一些细微的区别。

注:注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。
    无法对一个头文件中的一段代码作pragma once声明,而只能针对文件。

 #ifndef _AFILE_H_
 #define _AFILE_H_
 ...... // 声明,定义语句
 #endif

3.将类的声明和实现分开,即将一个类分为.h文件和.cpp文件
  因为类的使用者不关心类是怎么实现的,只关注类有什么功能。另外,一个.h文件可以被多个.cpp文件调用。

4.const成员函数
   如果你不想让某个成员函数修改成员变量的值,那么可以将这个成员函数声明为const。
   在编程时一定尽量多用const,对于不应当改变对象的成员函数都应该声明为const,这样你的程序如果试图去修改该对象的成员变量,编译器就会提示错误。从而达到帮你查错的目的。

注:声明和实现都要加const
    int Add() const;
    int A::Add() const {....//实现}

   非常量成员函数不能被常量对象调用。但,构造函数和析构函数例外,他们从不定义为常量成员,但可以被常量对象自动调用。
   const A a; //常量对象
   const对象只能调用const成员函数。
   非常量对象可以调用常量成员函数。
 

5.函数模板 template <class T, class M> T fun(T n, M p){....}  或者template <typename T> T fun(T n){....}
 
注:就可以理解为生成函数的模板。根据给定的不同的数据类型,生成对应的函数。
    模板只改变程序的大小,不改变编译程序的大小。

6.循环中放入switch语句,可以制造多选项的菜单。

7.用已有对象创建对象时,才会调用复制构造函数。

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

1.栈和堆  9.1-9.2-9.3这三节中讲述得很清楚。
  堆栈一般不能放在一起说,堆就是堆,栈就是栈。
栈和堆的比较:
1.内存申请方式不同
  栈由系统自动分配。
  堆由程序员自己申请。

2.系统响应不同
  栈的剩余空间符合要求,则正常分配,否则溢出。系统自动管理。
  堆是在操作系统的内存空闲地址链表中寻找合适大小的空间。需要new和delete。
3.空间大小不同
  栈的大小是程序开始就定义好了的。
  堆是不连续的内存区域,然后用链表串联起来。

4.执行效率不同
  栈速度快,堆慢但是用起来方便。

5.执行函数时的不同
 

2.内存泄露  
  由于使用new创建的内存空间是在堆中,且不会被系统释放,如果指向该区域的指针所指向的值被修改而指向别的地方或定义该指针的函数结束并返回时,指针就会消失。这样,这块内存地址就会处于游离状态,在程序结束之前是无法被访问的。这种情况就叫内存泄露。

3.实例化一个类占用多少内存
  一般根据类对象的成员变量来决定,假如一个类有两个int类型的成员变量,那么该对象占8个字节的空间。
  那么可以这样认为,程序运行需要先将一部分运行代码加载到内存中,在程序运行过程中对象的数据都是保存在另一个地方的。也就是说一个程序在内存中分为:代码部分和数据部分。
 
另外:栈区 + 堆区 + 寄存器区 + 全局区 + 文字常量区 + 程序代码区 = 运行中的程序
    栈区:一般存放函数的参数值、局部变量的值
    堆区:由程序员分配释放。
    全局区:全局变量和静态变量,初始化的在一块区域,未初始化的在一块区。
    文字常量区:常量字符串存放在这里。
    程序代码区:存放函数的二进制代码。19:27 2015/3/10


4.Human human 这样实例化的数据是保存在栈中的。其会在超出作用域时(比如遇到右大括号),自动调用析构函数释放该对象所占用的内存。
 
   Human* p = new Human 这样实例化的数据是保存在堆中的。其占用的内存要手动释放,否则该对象所占用的内存直到程序结束才会被系统回收。

5.(*p).get(); 和 p->get(); 这两个语句是相同的。
  (*p).get() 是使用*读取p内存地址中的值,即堆中对象,再使用“.”来访问成员函数get。
   p->get() 是用"->"运算符来实现指针间接访问对象成员。该符号可以实现读取对象的内存地址并且访问对象的成员。

6.在构造函数中也可以开辟堆中空间,但是实际程序中一个在堆中创建的对象,会为它的所有数据成员提供保存的空间。故这种开辟堆中空间是没有意义的。

7.this变量,它记录每个对象的内存地址,然后通过"->"来访问自己对象的成员。

8.常量指针,指向的地址是不可以改变的,但是对应对象的值是可以改变的。
  指向常量的指针,指向的目标不能被修改,自杀可以修改。

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

指针与引用区别:
  指针可以为空,引用不行。
  指针可以被赋值,引用不行。
  指针可以指向堆中空间,引用不行。


1.引用,就是别名,是常量,与原名用法一模一样。

2.堆中的对象一般由指针调用,故用不上别名、

3.因为引用只能指向一个对象,当你想用同一个变量指向多个用一类型对象,这时用指针。

4.
A func()  //按值返回的func函数
{
   A *p=new A(99);    //创建一个堆中对象并用p指向它同时初始化
   //该对象的成员变量x的值为99
   cout<<"p:"<<p<<endl;    //输出p中保存的值
   return *p;
}

A& r = func();

解释:r别名所指向的地址是*p对象的一个拷贝副本,放在栈中。也就是按值传值都是返回对象的一个拷贝。显然,上面出现了内存泄露。

所以,写代码时,注意多用传地址的方式,除非特殊要求就用传值,并且不建议在函数中创建堆中对象,这样容易出现问题(空引用,使用者忘记释放堆空间),最好做到,在哪里创建在哪里释放,谁创建谁释放。

posted on 2015-03-30 21:48  奔跑的小河  阅读(123)  评论(0编辑  收藏  举报