Coursera課程筆記----C++程序設計----Week1&2

從C走進C++(Week1&2)

函數指針

基本概念

  • 程序運行期間,每一個函數都會佔用一段連續的內存空間
  • 函數名就是該函數所佔內存區域的起始地址(入口地址)
  • 能夠將函數的入口地址賦給指針變量,使該指針變量指向該函數,經過指針變量就能夠調用這個函數
  • 這種指向函數的指針變量被稱爲「函數指針

定義形式

  • 類型名(* 指針變量名)(參數類型1,參數類型2,......)
int (*pf)(int , char);
//pf爲一個函數指針,它所指向的函數的返回值是int,2個參數一個是int類型一個是char類型

使用方法

  • 能夠用一個原型匹配的函數的名字給一個函數指針賦值
  • 經過函數指針調用他所指向函數
    • 函數指針名 (實參表)
#include <stdio.h>
void PrintMin(int a, int b)
{
  if(a < b)
    printf("%d",a);
  else
    printf("%d",b);
}
int main()
{
  void(* pf)(int, int);
  int x = 4, y = 5;
  pf = PrintMin;
  pf(x,y);
  return 0;
}
//pf指針指向PrintMin

qsort庫函數

  • 對數組排序,須要知道
    1. 數組起始地址
    2. 數組元素個數
    3. 每一個元素的大小(從而得出每一個元素的地址)
    4. 元素誰前誰後的規則
void qsort(void *base, int nelem, unsigned int width, int(* pfCompare)(const void*,const void*));
//base:待排序數組的起始地址
//nelem:待排序數組的元素個數
//width:待排序數組的每一個元素的大小(以字節爲單位)
//pfCompare:比較函數的地址
//pfCompare:函數指針,它指向一個「比較函數」,該比較函數的形式以下
//int 函數名 (const void * elem1,const void * elem2);
//比較函數是程序員本身編寫的
  • 排序就是一個不斷比較並交換位置的過程
  • qsort函數在執行期間,會經過pfCompare指針調用「比較函數」,調用時將要比較的兩個元素的地址傳給「比較函數」,而後根據「比較函數」返回值判斷哪一個應該排在前面
  • 比較函數編寫規則
    • 若是*elem1應該在前,函數返回負整數
    • 若是*elem2應該在前,函數返回正整數
    • 若是無所謂先後,函數返回0
  • 實例
    • 功能:調用qsort庫函數,將一個unsigned int數組按照個位數從小到大進行排序
#include <stdio.h>
#include <stdlib.h>

int MyCompare(const void * elem1,const void * elem2)
{
  unsigned int * p1, * p2;
  p1 = (unsigned int *) elem1; //"*elem1" 非法,編譯器不知道void指針指向的元素有多少個字節
  p2 = (unsigned int *) elem2; //"*elem2" 同上
  return (*p1 % 10) - (*p2 % 10);
}
#define NUM 5
int main()
{
  unsigned int an[NUM] = (8,123,11,10,4);
  qsort(an,NUM,sizeof(unsigned int),MyCompare);
  for(int i=0;i<NUM;i++)
    printf("%d",an[i]);
  return 0;
}

命令行參數

以命令行方式運行程序

  • notepad sample.txt
    • notepad程序如何得知,用戶在以命令行方式運行它的時候,後面跟着什麼參數?

命令行參數

  • 用戶在CMD窗口輸入可執行文件名的方式啓動程序時,跟在可執行文件名後面的那些字符串,稱爲「命令行參數」。
  • 命令行參數能夠有多個,用空格分隔
  • 舉例
    • copy file1.txt file2.txt
    • "copy","file1.txt","file2.txt"就是命令行參數
  • 如何得到命令行參數
    • argc (argument counter):表明啓動程序時,命令行參數的個數。C/C++語言規定,可執行程序程序自己的文件名,也算一個命令行參數,所以,argc的值至少是1
    • argv (argument vector):指針數組,其中的每一個元素都是一個char* 類型的指針,該指針指向一個字符串,這個字符串裏就存放着命令行參數
    • 提示: argument是實參,parameter是形參
    • 因爲命令行參數之間用空格分隔,若是其自己就含有空格,則可用雙引號括起來
int main(int argc, char * argv[])
{
  ...
}

位運算

基本概念

  • 位運算:
    • 用於對整數類型(int, char, long...)變量中的某一位(bit)或者若干位進行操做。好比:
      1. 判斷某一位是否爲1
      2. 只改變其中某一位,而保持其餘位都不變
    • C/C++語言提供了六種位運算符來進行位運算操做:
      • & 按位與(雙目)
      • |按位或(雙目)
      • ^ 按位異或 (雙目)
      • ~ 按位非(取反)(單目)
      • <<左移(雙目)
      • >>右移(雙目)

按位與「&」

  • 將參與運算的兩數,各對應的二進制位進行操做,只有對應的兩個二進制位均爲1時,結果對應的二進制位才爲1,不然爲0ios

  • 一般用來將某變量中的某些位清0切同時保留其餘位不變,也可用來獲取某變量中的某一位程序員

按位或「|」

  • 將參與運算的兩操做數各對應的二進制位進行操做,只有對應的兩個二進制位都爲0時,結果的對應二進制位纔是0,不然爲1
  • 一般用來將某變量中的某些位置1且保留其餘位不變

按位異或「^」

  • 將參與運算的兩操做數各對應的二進制位進行異或操做,只有對應的兩個二進制位不相同時,結果的對應二進制位纔是1,不然爲0
  • 一般用來將變量中的某些位取反且保留其餘位不變
  • 特色:若是有ab=c➡️cb=a 、c^a=b
  • 能實現不經過臨時變量就交換兩個變量的值
int a = 5, b = 7;
a = a ^ b;
b = b ^ a;
a = a ^ b;

按位非「~」

  • 將操做書中的二進制位0變成1,1變成0

左移運算符「<<」

  • a << b
  • 將a的各二進制位所有左移b位後獲得的值。左移時,高位丟棄,低位補0,a自己的值不會被更改
  • 左移1位就等於*2,左移n位就等於* $2^n$

右移運算符「>>」

  • a >> b
  • 將a的各二進制位所有右移b位後獲得的值。右移時,移出右邊的位會被丟棄,a自己的值不會被更改
  • 對於有符號數,右移時,符號位將一塊兒移動
  • 大多C/C++編譯器規定,若是原符號位位1,右移時高位補1,不然補0。
  • 左移1位就等於/2,左移n位就等於/ $2^n$,而且將結果往小取整

引用

引用的概念

  • 下面的寫法定義了一個引用,並將其初始化爲引用某個變量算法

    類型名 & 引用名 = 某變量名編程

int n = 4;
int & r = n; //r引用了n,r的類型是int &
  • 某個變量的引用,等價於這個變量,至關於該變量的一個別名數組

  • 定義引用時必定要將其初始化成引用某個變量數據結構

  • 初始化後,他就一直引用該變量,不會再引用別的變量了數據結構和算法

  • 引用只能引用變量,不能引用常量和表達式模塊化

引用的應用

  • C語言中,如何編寫交換兩個整形變量值的函數?
void swap(int a, int b)
{
 int tmp;
 tmp = a; a = b; b = tmp;
}
int n1,n2;
swap(n1,n2); //n1n2的值不會被交換,形參的改變沒法影響實參

void swap(int *a,int *b)
{
 int tmp;
 tmp = *a; *a = *b; *b = tmp;
}
int n1,n2;
swap(&n1,&n2); //n1n2的值被交換,可是多了不少符號,比較麻煩
void swap(int &a, int &b)
{
  int tmp;
  tmp = a; a = b; b = tmp;
}
int n1,n2;
swap(n1,n2); //n1n2的值被交換,因爲a和b是n1和n2的引用,於是能夠直接修改
  • 引用做爲函數的返回值
int n = 4;
int & SetValue() {return n;}
int main()
{
  SetValue() = 40; //函數的返回值是引用,就能夠把函數寫在等號左邊,能夠直接賦值
  cout<<n;//輸出:40
  return 0;
}

常引用

  • 定義引用時,前面加const關鍵字,即爲「常引用」
int n;
const int & r = n;
//r的類型是const int &
  • 特色:不能經過常引用去修改其引用的內容

常引用和很是引用的轉換

  • const T & 和 T &是不一樣的類型(T爲int,char等類型)
  • T & 類型的引用或T類型的變量能夠用來初始化const T & 類型的引用
  • const T 類型的常變量和const T & 類型的引用則不能用來初始化 T & 類型的引用,除非進行強制類型轉換

const關鍵字的用法

  1. 定義常量
const int MAX_VAL = 23;
const double Pi = 3.14;
  1. 定義常量指針
    • 不可經過常量指針修改其指向的內容
    • 不能把常量指針賦值給很是量指針,反過來能夠
    • 函數參數爲常量指針時,可避免函數內部不當心改變參數指針所指地方的內容
int n,m;
const int *p = &n;
*p = 5; //編譯錯誤
n = 4; //正確
p = &m; //正確,能夠改變常量指針指向的對象
const int * p1; int * p2;
p1 = p2;//正確
p2 = p1;//錯誤
p2 = (int *)p1;//正確,經過強制類型轉換
void MyPrintf(const char *p)
{
  strcpy(p,"this");//編譯錯誤
  printf("%s",p);//正確
}
  1. 定義常引用
    • 不能經過常引用修改其引用的變量
int n;
const int & r = n;
r = 5;//編譯錯誤
n = 4;//正確

動態內存分配

用new運算符實現動態內存分配

  • 第一種用法,分配一個變量
    • P = new T;
    • T是任意類型名,P是類型爲**T ***的指針
    • 動態分配出一片大小爲sizeof(T)字節的內存空間,而且將該內存空間的起始地址賦值給P
int *pn;
pn = new int;
*pn = 5;
  • 第二種用法,分配一個數組
    • P = new T[N];
    • T:任意類型名
    • P:類型爲T *的指針
    • N:要分配的數組元素的個數,能夠是整形表達式
    • 動態分配出一片大小爲N*sizeof(T)字節的內存空間,並將該內存空間的起始地址賦值給P
  • 動態分配數組實例
int *pn;
int i = 5;
pn = new int[i * 20];
pn[0] = 20;
pn[100] = 30;//雖然編譯正確,但運行時會出現數組越界
  • new 運算符的返回值類型
    • new T; new T[n];
    • 這兩個表達式返回值的類型都是 T*
    • int *p = new int;

用delete運算符釋放動態分配的內存

  • 用「new」動態分配的內存空間,要用「delete」運算符進行釋放
    • delete 指針; //該指針必須指向new出來的空間
int *p = new int;
*p = 5;
delete p;
delete p; //致使異常,一片空間不可以被delete屢次

用delete運算符釋放動態分配的數組

  • 用「delete」釋放動態分配的數組,要加「[]」
    • delete [] 指針; //該指針必須指向new出來的數組
int *p = new int[20];
p[0] = 1;
delete [] p;

內聯函數,函數重載和函數缺省參數

內聯函數

  • 函數調用存在時間開銷。若是函數自己只有幾條語句且執行很是快,並且函數被反覆執行屢次,相比其運行時間,調用函數所產生的時間開銷就會很大。函數

  • 爲了減小該開銷,引入了內聯函數機制。編譯器處理對內聯函數的調用語句時,是將整個函數的代碼插入到調用語句處,而不會產生調用函數的語句。ui

  • 在函數定義前面加「inline」關鍵字,便可定義內聯函數

  • 缺點是可執行程序的體積會增大

函數重載

  • 一個或多個函數,名字相同,然而參數個數參數類型不相同,這叫作函數重載

    • 如下三個函數是重載關係:

      int Max(double f1,double f2){ }
      int Max(int n1,int n2){ }
      int Max(int n1,int n2,int n3){ }
    • 函數重載簡化函數命名

    • 編譯器根據調用語句中的實參的個數和類型判斷應該調用哪一個函數

函數缺省參數

  • C++中,定義函數的時候可讓最右邊的連續若干個參數有缺省值,那麼調用函數的時候,若相應位置不寫參數,參數就是缺省值
void func(int x1, int x2 = 2, int x3 = 3){ }

func(10);//等效於func(10,2,3)
func(10,8);//等效於func(10,8,3)
func(10,,8);//編譯錯誤,只能最右邊的連續若干個參數缺省
  • 函數參數可缺省的目的在於提升程序的可擴充性
  • 若是某個寫好的函數要添加新的參數,而原先那些調用該函數的語句,未必須要使用新增的參數,那麼爲了避免對原先那些函數調用語句的修改,就可使用缺省參數

面向對象程序設計方法

結構化程序設計

  • 複雜的大問題➡️層層分解/模塊化➡️若干子問題
  • 自頂向下,逐步求精
  • 程序 = 數據結構(變量)+算法(函數)
  • 在結構化程序設計中,數據結構和算法沒有直接關係
  • 遇到的問題
    • 理解難
    • 修改難
    • 查錯難
    • 重用難

面向對象的程序設計

  • 軟件設計的目標:更快,更正確,更經濟
  • 面向對象的程序設計 = 類 + 類 + …… + 類
  • 設計程序的過程➡️設計的過程
  • 對一類事物進行抽象,提煉出共同屬性(數據結構)和行爲(函數),將數據結構和算法封裝(捆綁)在一塊兒,變成類。

面嚮對象語言的發展歷程

  • 第一個面嚮對象語言:Simula

    • 1967年發佈Simula 67
    • 提出了類(class)和子類(subclass)的概念
  • 第二個面嚮對象語言:Smalltalk

  • 1983年 C++

  • 1995年 JAVA

  • 2003年 C#

C++標準的發展

  • 1989年 C++2.0
  • 1994年 ANSI C++
  • 1998年 C++98
    • 加入STL(Standard Template Library)-泛型設計
  • 2003年 C++03
  • 2011年 C++11
  • 2014年 C++14
  • 2017年 C++17
  • 2020年 C++20

從客觀事物抽象出類

  • 寫一個程序,輸入矩形的寬和高,輸出面積和周長
    • 矩形的屬性——寬和高兩個變量
    • 矩形的操做——設置寬和高,計算面積計算周長
  • 類的成員=成員變量+成員函數
  • 類就是一個帶函數的結構體
  • 類定義的變量➡️類的實例➡️對象
class CRectangle{
  public:
  int w,h;
  
  void Init(int w_, int h_)
  {
    w = w_; h = h_;
  }
  int Area()
  {
    return w*h;
  }
  int Perimeter()
  {
    return 2*(w+h);
  }
};

int main()
{
  int w,h;
  CRectangle r; //r是一個對象
  cin>>w>>h;
  r.Init(w,h);
  cout<<r.Area()<<endl<<r.Perimeter();
  return 0;
}
  • 對象的內存分配

    • 對象的內存空間
      • 對象的大小 = 全部成員變量的大小之和
      • e.g. CRectangle類的對象,sizeof(CREctangle) = 8
    • 每一個對象各有本身的存儲空間
      • 一個對象的某個成員變量被改變,不會影響到其餘的對象
  • 對象間的運算

    • 對象之間能夠用‘=’進行賦值
    • 不能用== != > < >= <=進行比較
      • 除非這些運算符通過了「重載「
  • 訪問類的成員變量和成員函數

    • 用法1:對象名.成員名
    • 用法2:指針->成員名
    • 用法3:引用名.成員名
  • 類的成員函數的另外一種寫法

    • 成員函數體和類的定義分開寫,在類內只聲明,在類外詳細定義(要加::)

類成員的可訪問範圍

  • 關鍵字——類成員可被訪問的範圍
    • private:指定私有成員,只能在成員函數內被訪問
    • public:指定公有成員,能夠在任何地方被訪問
    • protected:指定保護成員
  • 三種關鍵字出現的次數和前後次序都沒有限制
  • 若是缺省,就默認爲私有成員
  • 對象成員的訪問權限
    • 類的成員函數內部,能夠訪問:
      • 當前對象的所有屬性和函數
      • 同類其餘對象的所有屬性和函數
    • 類的成員函數之外的地方
      • 只能訪問該類對象的公有成員
  • 設置私有成員的目的
    • 強制對成員變量的訪問必定要經過成員函數進行
    • 避免程序出錯,而且易於對程序進行修改
  • 設置私有成員的機制——隱藏

編程做業

Quiz1 簡單的學生信息處理程序實現

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<string>
#include<string.h>
//雖然我本地編譯用最上面那個頭文件就過了,但上傳上去好像必需要加上後面這四個才能經過……行吧
using namespace std;
class student{
private:
    char name[10];
    int age;
    char number[10];
    unsigned int gradeAverage;

public:
    void gradeCalculate(unsigned int a,unsigned int b,unsigned int c,unsigned int d)
    {
        gradeAverage = (a+b+c+d)/4;
    }
    student(const char* name_, int age_, const char* number_)
    {
        strcpy(name,name_);
        age = age_;
        strcpy(number,number_);
    }
    void getAll()
    {
        cout << name <<',' << age << ',' << number << ',' << gradeAverage << endl;
    }
};

int main()
{
    char name[10] = {'\0'},number[10] = {'\0'};
    int age;
    unsigned int grade1,grade2,grade3,grade4;
    //此處的cin.get()用於把逗號吞掉
    cin.getline(name,10,',');
    cin >> age;
    cin.get();
    cin.getline(number,10,',');
    cin >> grade1;
    cin.get();
    cin>> grade2;
    cin.get();
    cin >> grade3;
    cin.get();
    cin >> grade4;
    
    student* a = new student(name,age,number);
    a->gradeCalculate(grade1,grade2,grade3,grade4);

    a->getAll();
    delete(a);
    return 0;
}
相關文章
相關標籤/搜索