C++自学笔记(1)

1、int型变量赋值给指针是非法的,但允许把0或编译时可获得0值的const量赋给指针

根据测试,上面那句话的意思是:

 const int address=10 ;    
    int *ds=address; 

或者

    int *ds=10;

报错。

 const int address=0;
    int *ds=address;

或者

int *ds=0;

不报错

但是,我在DEVC++中测试指针时,以下代码出错:

 int *ds=0;

cout<<*ds;
即输出空的指程序崩溃:空指针 是指向内存位置0的指针 ,输出一个空指针即 访问内存地址0 ,通常这个位置 是操作系统的, 在某些系统中 内存地址0是不可读不可写的, 在某些系统中 ,内存地址0是可读但不可写的,所以说 会是程序崩溃 这个说法有些片面

2、“野指针”不是NULL指针,是指向“垃圾”内存(不可用内存)的指针。

空指针 是指向内存位置0的指针 输出一个空指针 讲访问这个空指针 即 访问内存地址0 通常这个位置 是操作系统的 在某些系统中 内存地址0是不可读不可写的 在某些系统中 内存地址0是可读但不可写的,所以说 会是程序崩溃 这个说法有些片面

3、void* 指针只支持几种有限的操作:

与另一个指针进行比较;

向函数传递 
void* 指针或从函数返回 void* 指针;

给另一个 void* 指针赋值。

不允许使用 void* 指针操纵它所指向的对象。

4、 
     -21 % -8; //  ok: result is -5 
     21 % -5;  //  machine-dependent: result is 1 or -4    
     -21 / -8; //  ok: result is 2 
     21 / -5;  //  machine-dependent: result -4 or -5 

5、不应该串接使用关系操作符 
关系操作符(<、<=、>、<=)具有左结合特性。事实上,由于关系操作符返
回bool类型的结果,因此很少使用其左结合特性。如果把多个关系操作符串接
起来使用,结果往往出乎预料: 
     // oops! this condition does not determine if the 3 values are unequal 
     if (i < j < k) { /* ... */ } 

以上写法错误。

6、千万不能返回局部变量的引用,和返回局部对象的引用一样,返回指向局部对象的指针也是错误的

7、主函数 main 不能调用自身。 

8、默认实参

string screenInit(string::size_type height = 24, string::size_type width = 80, char background = ' ' );

默认实参的初始化式 
默认实参可以是任何适当类型的表达式: 
     string::size_type screenHeight(); 
     string::size_type screenWidth(string::size_type); 
     char screenDefault(char = ' '); 
     string screenInit( 
         string::size_type height = screenHeight(), 
         string::size_type width = screenWidth(screenHeight()), 
         char background = screenDefault()); 
 
如果默认实参是一个表达式,而且默认值用作实参,则在调用函数时求解该表达
式。例如,每次不带第三个实参调用函数 screenInit 时,编译器都会调用函数 
screenDefault 为 background 获得一个值。 

注意事项:

如果在函数定义的形参表中提供默认实参,那么只有在包含该函数定义的源文件
中调用该函数时,默认实参才是有效的。 

9、区别函数的多次声明和函数的重载。

函数的多次声明:如果两个函数声明的返回类型和形参表完全匹配,则将第二个函数声明视为第一个的重复声明。如果两个函数的形参表完全相同,但返回类型不同,则第二个声明是错误的。

函数重载:函数重载是指在同一作用域内,可以有一组具有相同函数名不同参数列表的函数,这组函数被称为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。main()函数不能重载!函数重载,要求返回值类型一样,如果返回值类型不一样,不会报错,但已不是原来函数的重载,而是另一个函数了。

10、如果容器存储类类型的对象,那么只有当其元素类型提供默认构造函数时,容器才能使用这种构造函数。尽管有一些类没有提供默认构造函数,但大多数类类型都会有。例如,假设类 Foo 没有默认构造函数,但提供了需要一个 int 型形参的构造函数。现在,考虑下面的声明:
    vector<Foo> empty;     // ok: no need for element defaultconstructor
    vector<Foo> bad(10);   // error: no default constructor for Foo
    vector<Foo> ok(10, 1); // ok: each element initialized to 1

注意,在指定容器元素为容器类型时,必须如下使用空格:
    vector< vector<string> > lines; // ok: space required between close>
    vector< vector<string>> lines; // error: >> treated as shiftoperator
    必须用空格隔开两个相邻的 > 符号,以示这是两个分开的符
 号,否则,系统会认为 >> 是单个符号,为右移操作符,并导 致编译时错误。

关键概念:容器元素都是副本
在容器中添加元素时,系统是将元素值复制到容器里。类似地,使用一段元素初始化新容器时,新容器存放的是原始元素的副本。被复制的原始值与新容器中的元素各不相关,此后,容器内元素值发生变化时,被复制的原值不会受到影响,反之亦然。

在 vector 容器中添加元素可能会导致整个容器的重新加载,这样的话,该容器涉及的所有迭代器都会失效。即使需要重新加载整个容器,指向新插入元素后面的那个元素的迭代器也会失效。 任何 insert 或 push 操作都可能导致迭代器失效。当编写循环将元素插入到 vector 或 deque 容器中时,程序必须确保迭代器在每次循环后都得到更新。

//

swap 操作实现交换两个容器内所有元素的功能。要交换的容器的类型必须匹配:操作数必须是相同类型的容器,而且所存储的元素类型也必须相同。调用了 swap 函数后,右操作数原来存储的元素被存放在左操作数中,反之亦然。

    vector<string> svec1(10); // vector with 10 elements
    vector<string> svec2(24); // vector with 24 elements

    svec1.swap(svec2);
执行 swap 后,容器 svec1 中存储 24 个 string 类型的元素,而 svec2 则存储 10 个元素。

swap方法实现过程中可能是交换了指针,应该不是各个元素依次交换。

//

为了支持快速的随机访问,vector 容器的元素以连续的方式存放——每一个元素都紧挨着前一个元素存储。
    已知元素是连续存储的,当我们在容器内添加一个元素时,想想会发生什么事情:如果容器中已经没有空间容纳新的元素,此时,由于元素必须连续存储以便索引访问,所以不能在内存中随便找个地方存储这个新元素。于是,vector 必须重新分配存储空间,用来存放原来的元素以及新添加的元素:存放在旧存储空间中的元素被复制到新存储空间里,接着插入新元素,最后撤销旧的存储空间。如果 vector 容器在在每次添加新元素时,都要这么分配和撤销内存空间,其性能将会非常慢,简直无法接受。

//

选择容器的提示
下面列举了一些选择容器类型的法则:
  _ 1. 如果程序要求随机访问元素,则应使用 vector 或 deque 容器。
   _2. 如果程序必须在容器的中间位置插入或删除元素,则应采用 list 容器。
   _3. 如果程序不是在容器的中间位置,而是在容器首部或尾部插入或删除元素,则应采用 deque 容器。
   _4. 如果只需在读取输入时在容器的中间位置插入元素,然后需要随机访问元 素,则可考虑在输入时将元素读入到一个 list 容器,接着对此容器重新 排序,使其适合顺序访问,然后将排序后的 list 容器复制到一个 vector容器。

11、使用泛型算法必须包含 algorithm 头文件:

    #include <algorithm>
    标准库还定义了一组泛化的算术算法(generalized numeric algorithm),其命名习惯与泛型算法相同。使用这些算法则必须包含 numeric 头文件:
    #include <numeric>

泛型算法:

_1、find与find_first_of 

find:

int search_value = 42;   vector<int>::const_iterator result = find(vec.begin(), vec.end(), search_value);

find_first_of :

find_first_of 算法带有两对迭代器参数来标记两段元素范围,在第一段范围内查找与第二段范围中任意元素匹配的元素,然后返回一个迭代器,指向第一个匹配的元素。如果找不到元素,则返回第一个范围的 end 迭代器。

list<string>::iterator it = roster1.begin();  

    while   ((it = find_first_of(it, roster1.end(),roster2.begin(), roster2.end())) != roster1.end()) {......操作}

_2、另一个简单的只读算法是 accumulate,该算法在 numeric 头文件中定义。假设 vec 是一个 int 型的 vector 对象,下面的代码:
    // sum the elements in vec starting the summation with the value 42
    int sum = accumulate(vec.begin(), vec.end(), 42);
    将 sum 设置为 vec 的元素之和再加上 42。accumulate 带有三个形参。头两个形参指定要累加的元素范围。第三个形参则是累加的初值

_3、fill函数与fill_n函数

fill函数:写入操作:fill(vec.begin(),vec.end(),10);把迭代器范围内的数据写为10.

 fill_n 函数:  fill_n 函数带有的参数包括:一个迭代器、一个计数器以及一个值。该函数从迭代器指向的元素开始,将指定数量的元素设置为给定的值。fill_n 函数假定对指定数量的元素做写操作是安全的。初学者常犯的错误的是:在没有元素的空容器上调用 fill_n 函数(或者类似的写元素算法)。以下代码将会出错:
    vector<int> vec; // empty vector    
    fill_n(vec.begin(), 10, 0);

为了 确保算法有足够的元素存储输出数据的一种方法是使用插入迭代器back_inserter。插入迭代器是可以给基础容器添加元素的迭代器,使用 back_inserter 的程序必须包含 iterator 头文件。fill_n (back_inserter(vec), 10, 0);这样便不会出现上述错误。

_4、   copy、replace和replace_copy

copy:

copy (ilst.begin(), ilst.end(), back_inserter(ivec));copy 从输入范围中读取元素,然后将它们复制给目标 ivec。
    当然,这个例子的效率比较差:通常,如果要以一个已存在的容器为副本创建新容器,更好的方法是直接用输入范围作为新构造容器的初始化式:
    vector<int> ivec(ilst.begin(), ilst.end());

replace:

每一个等于第一值的元素替换成第二个值。 replace(ilst.begin(), ilst.end(), 0, 42);这个调用将所有值为 0 的实例替换成 42。

replace_copy:

如果不想改变原来的序列,则调用 replace_copy。这个算法接受第三个迭代器实参,指定保存调整后序列的目标位置。  
    vector<int> ivec;   // use back_inserter to grow destination as needed

    replace_copy (ilst.begin(), ilst.end(), back_inserter(ivec), 0, 42);

    调用该函数后,ilst 没有改变,ivec 存储 ilst 一份副本,而 ilst 内所有的 0 在 ivec 中都变成了 42。

_5、对容器元素的重新排序算法

sort:sort 算法带有两个迭代器实参,指出要排序的元素范围。这个算法在容器各个元素之间使用小于(<)操作符比较元素。

sort(words.begin(), words.end());

unique :
    让容器中的所有元素都只保留一个副本。unique 算法很适合用于解决这个问题,它带有两个指定元素范围的迭代器参数。该算法“删除”相邻的重复元素,然后重新排列输入范围内的元素,并且返回一个迭代器,表示无重复的值范围的结束。

注意:调用 unique“删除”了相邻的重复值。给“删除”加上引号是因为 unique 实际上并没有删除任何元素,而是将无重复的元素复制到序列的前端,从而覆盖相邻的重复元素。unique 返回的迭代器指向超出无重复的元素范围末端的下一位置。实际上容器中元素的个数并没有改变。

erase:用于容器中元素的删除操作算法不直接修改容器的大小。erase属于容器操作,不属于泛型算法,要添加或删除元素,则必 须使用容器操作。

words.erase(end_unique, words.end());这个函数调用从 end_unique 指向的元素开始删除,直到 words 的最后一个元素也删除掉为止。

12、  static 成员是类的组成部分但不是任何对象的组成部分,因此,static 成员函数没有 this 指针。

13、 类也可以定义 mutable 或 static 成员。mutable 成员永远都不能为const;它的值可以在 const 成员函数中修改。static 成员可以是函数或数据,独立于类类型的对象而存在。

14、不管类是否定义了自己的析构函数,编译器都自动执行类中非static 数据成员的析构函数。

15、友元可以访问类的 private 和 protected 数据。 友元关系不能继承。基类的友元对派生类的成员没有特殊访问权限。如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。

16、 如果基类定义 static 成员,则整个继承层次中只有一个这样的成员。无论从基类派生出多少个派生类,每个 static 成员只有一个实例。static 成员遵循常规访问控制:如果成员在基类中为 private,则派生类不能访问它。假定可以访问成员,则既可以通过基类访问 static 成员,也可以通过派生类访问 static 成员。一般而言,既可以使用作用域操作符也可以使用点或箭头成员访问操作符。
    struct Base

   {
    static void statmem(); // public by default
    };


    struct Derived : Base

   {
    void f(const Derived&);
    };


    void Derived::f(const Derived &derived_obj)

    {

    Base::statmem();      // ok: Base defines statmem
    Derived::statmem();   // ok: Derived in herits statmem
    // ok: derived objects can be used to access static from base
    derived_obj.statmem();     // accessed through Derived object
    statmem();                 // accessed through this class

   }

17、派生类到基类的转换

    像继承的成员函数一样,从派生类到基类的转换可能是也可能不是可访问的。转换是否访问取决于在派生类的派生列表中指定的访问标号。
    要确定到基类的转换是否可访问,可以考虑基类的 public 成员是否访问,如果可以,转换是可访问的,否则,转换是 不可访问的。
    如果是 public 继承,则用户代码和后代类都可以使用派生类到基类的转换。如果类是使用 private 或 protected 继承派生的,则用户代码不能将派生类型对象转换为基类对象。如果是 private 继承,则从 private 继承类派生的类不能转换为基类。如果是 protected 继承,则后续派生类的成员可以转换为基类类型。 无论是什么派生访问标号,派生类本身都可以访问基类的 public 成员,因此,派生类本身的成员和友元总是可以访问派生类到基类的转换。

18、纯虚函数

C++语言为我们提供了一种语法结构,通过它可以指明一个 虚拟函数只是提供了一个可被子类型改写的接口。但是,它本身并不能通过虚拟机制被调用,这就是纯 虚拟函数(pure virtual function)。 纯虚函数是在 基类 中声明的虚函数,它可以在基类中有定义,而且 派生类 必须定义自己的实现方法。基类不能生成对象,可以使用指针或者引用派生类对象。基类不在 基类 中实现纯虚函数的方法是在函数原型后加“=0”
virtual void funtion1()=0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值