5.2-day02-C++/內聯/動態記憶體分配/引用/顯示型別轉換
阿新 • • 發佈:2018-11-29
九、
3.內聯
1)編譯器用函式的二進位制程式碼替換函式呼叫語句,減少函式呼叫的時間開銷。這種優化策略成為內聯。
2)頻繁呼叫的簡單函式適合內聯,而稀少呼叫的複雜函式不適合內聯。
3)遞迴函式無法內聯。
4)通過inline關鍵字,可以建議編譯對指定函式進行內聯,但是僅僅是建議而已。
inline void foo (int x, int y){...}
十、C++的動態記憶體分配
malloc/calloc/realloc/free
1.new/delete:對單個變數進行記憶體分配/釋放
2.new[ ]/delete[ ]:對陣列進行記憶體分配/釋放
十一、引用
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
十二、顯示型別轉換運算子
C:目標型別變數 = (目標型別)源型別變數;
1.靜態型別轉換
static_cast<目標型別> (源型別變數)
如果在目標型別和源型別之間某一個方向上可以做隱式型別轉換,那麼在兩個方向上都可以做靜態型別轉換。反之如果在兩個方向上都不能做隱式型別轉換,那麼在任意一個方向上也不能做靜態型別轉換。
int* p1 = ...;
void* p2 = p1;
p1 = static_cast<int*> (p2);
char c;
int i = c;
如果存在從源型別到目標型別的自定義轉換規則,那麼也可以使用靜態型別轉換。
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);
十三、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.樹立面向物件的程式設計思想。
++C
第二課 類和物件
一、什麼是物件
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)類的成員預設訪控屬性為私有,而結構的成員預設訪控屬性為公有。
1.c
1.cpp
const.cpp
【給一個擁有const屬性的指標或引用去常,去掉常屬性】 const 只讀屬性; const int a = 100; //常量優化 不管a是多少,a都為100,打出a的字面值,用100去優化;
const volatile int a = 100; 別做優化,會用a的值輸出; 用volatile保證不做暫存器優化,常量優化的值; 保證訪問的是記憶體中的值,而不是歷史值; new返回的是陣列的首地址;
new.cpp
三維陣列:二維陣列的陣列 int (*p) [4] = new int [3][4]; 用二維陣列的指標去接受三維陣列;
動態記憶體分配: string自動維護記憶體,無記憶體限制; “\0”,且自動新增 ‘\0’
對單個變數: 對陣列: malloc在堆裡釋放記憶體,只malloc不free, 稱為記憶體的洩露; int *pi = new int; new:對單個變數進行記憶體的分配; 野指標:存著一個地址,但裡面標識的記憶體已經被釋放; 以陣列方式new的,要用delete[] 刪掉; pi = new int [10]; ......delete[] pi; delete pi; 區域性釋放 delete[ ] pi; 全部釋放 pi = new int (1234); new的同時賦初值; 棧記憶體不能delete; pi = new (buf)int; 以buf為首的,int大小區域;
reinter.cpp
retref.cpp
int & foo (void) { return g_data; } // 200賦給g_data了,此時賦的是別名int & foo (void);
static.cpp
string.cpp
student.cpp
swap.cpp
指標:
void swap5 (const char** x, const char** y) 要用二級指標,即,const char ** x
引用型引數: void print ( const struct Student & s )
&符號避免拷貝的開銷;提高了效率; 避免拷貝,通過加const可以防止在函式中意外地修改實參的值, 同時還可以接受擁有常屬性的實參。
來自為知筆記(Wiz)
1.c
#include
<iostream>using namespace std;
int main (void) {
cout << "Hello, World !" << endl;
return 0;
}
1.cpp
用2011編譯器去編譯; g++ 11.cpp -std=c++0x
#include <iostream>
using namespace std;
int main (void) {
auto i
= 10;auto f = 3.14;
cout << i << ' ' << f << endl;
return 0;
}
const.cpp
強制型別轉換:放棄一切安全機制; 目標型別變數=(目標型別) 源型別變數; C++編譯器:靜態編譯器; 靜態:static_cast 動態:dynamic_cast
#include <iostream>
using namespace std;
int main (
void) {const volatile int a = 100;
// 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;
}
【給一個擁有const屬性的指標或引用去常,去掉常屬性】 const 只讀屬性; const int a = 100; //常量優化 不管a是多少,a都為100,打出a的字面值,用100去優化;
const volatile int a = 100; 別做優化,會用a的值輸出; 用volatile保證不做暫存器優化,常量優化的值; 保證訪問的是記憶體中的值,而不是歷史值; new返回的是陣列的首地址;
new.cpp
new/delete:對單個變數進行記憶體分配/釋放; new[]/delete[]:對陣列進行記憶體分配/釋放; new int[3][4] 返回的是一個二維陣列第一個元素的地址;
#include <iostream>
using namespace std;
int main (void) {
// int* pi = (int*)malloc (sizeof (int));
// free (pi);
int* pi = new int;
*pi = 1000;
cout << *pi << endl;
delete pi;
pi = NULL;
/*
*pi = 2000;
cout << *pi << endl;
*/
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;
pi = NULL;
pi = new int (1234);
cout << *pi << endl;
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;
}
三維陣列:二維陣列的陣列 int (*p) [4] = new int [3][4]; 用二維陣列的指標去接受三維陣列;
動態記憶體分配: string自動維護記憶體,無記憶體限制; “\0”,且自動新增 ‘\0’
對單個變數: 對陣列: malloc在堆裡釋放記憶體,只malloc不free, 稱為記憶體的洩露; int *pi = new int; new:對單個變數進行記憶體的分配; 野指標:存著一個地址,但裡面標識的記憶體已經被釋放; 以陣列方式new的,要用delete[] 刪掉; pi = new int [10]; ......delete[] pi; delete pi; 區域性釋放 delete[ ] pi; 全部釋放 pi = new int (1234); new的同時賦初值; 棧記憶體不能delete; pi = new (buf)int; 以buf為首的,int大小區域;
reinter.cpp
reinterpret_cast <> ( ) //重解釋型別轉換-同一個記憶體,從不同角度(不同指標型別去看),對風馬牛不相及的指標進行轉化;
#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] << ' ';
cout << endl;
float* q = reinterpret_cast<float*> (&i);
cout << *q << endl;
void* v = reinterpret_cast<void*> (i);
cout << v << endl;
return 0;
}
retref.cpp
int b = 10; int a = func(b); 返回右值; func(b) = a; 返回左值; foo ( ) = 200; //200賦給臨時變數;
#include <iostream>
using namespace std;
int g_data = 100;
int& foo (void) {
return g_data;
}
int& bar (void) {
int a = 100;
return a;
}
int& fun (void) {
int b = 200;
return b;
}
int& hum (int& a) {
return a;
}
int main (void) {
int data = foo ();
cout << data << endl; // 100
foo () = 200;
cout << g_data << endl;
foo () += 100;
++foo ();
cout << g_data << endl; // 301
int& a = bar ();
fun ();
cout << a << endl; // 200
return 0;
}
int & foo (void) { return g_data; } // 200賦給g_data了,此時賦的是別名int & foo (void);
static.cpp
指標p2進行靜態型別轉換: p1 = static_cast<int*> (p2);
#include <iostream>
using namespace std;
int main (void) {
int* p1 = NULL;
void* p2 = p1;
p1 = static_cast<int*> (p2);
return 0;
}
string.cpp
#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;
}
cout << (strcasecmp (s1.c_str (),
s3.c_str ()) == 0) << endl;
student.cpp
#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");
student.setAge (-100);
student.setName ("張飛");
student.setAge (20);
student.eat ("包子");
return 0;
}
swap.cpp
引用: swap3 (a, b); -> void swap3 (int& a, int& b) {......}
#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;
}
指標:
void swap5 (const char** x, const char** y) 要用二級指標,即,const char ** x
引用型引數: void print ( const struct Student & s )
&符號避免拷貝的開銷;提高了效率; 避免拷貝,通過加const可以防止在函式中意外地修改實參的值, 同時還可以接受擁有常屬性的實參。
來自為知筆記(Wiz)