5.2-day02-C++/内联/动态内存分配/引用/显示类型转换
九、
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
#include<iostream>
usingnamespace std;
int main (void){
cout <<"Hello, World !"<< endl;
return0;
}
![](https://img2018.cnblogs.com/blog/1167156/201811/1167156-20181129225744809-1440740540.png)
1.cpp
#include<iostream>
usingnamespace std;
int main (void){
auto i =10;
auto f =3.14;
cout << i <<' '<< f << endl;
return0;
}
![](https://img2018.cnblogs.com/blog/1167156/201811/1167156-20181129225745117-1688228954.png)
用2011编译器去编译;
g++ 11.cpp -std=c++0x
const.cpp
#include<iostream>
usingnamespace std;
int main (void){
constvolatileint a =100;
// a = 200;
constvolatileint* p1 =&a;
// *p1 = 200;
int* p2 =const_cast<int*>(p1);
*p2 =200;
cout <<*p2 << endl;// 200
cout << a << endl;// 200
// cout << 100 << endl;
return0;
}
![](https://img2018.cnblogs.com/blog/1167156/201811/1167156-20181129225745684-1681239220.png)
强制类型转换:放弃一切安全机制;
目标类型变量=(目标类型) 源类型变量;
C++编译器:静态编译器;
静态:static_cast
动态:dynamic_cast
【给一个拥有const属性的指针或引用去常,去掉常属性】
const 只读属性;
![](https://img2018.cnblogs.com/blog/1167156/201811/1167156-20181129225745932-851530438.png)
![](https://img2018.cnblogs.com/blog/1167156/201811/1167156-20181129225746143-1999706274.png)
const int a = 100; //常量优化
不管a是多少,a都为100,打出a的字面值,用100去优化;
const volatile int a = 100;
别做优化,会用a的值输出;
用volatile保证不做寄存器优化,常量优化的值;
保证访问的是内存中的值,而不是历史值;
new返回的是数组的首地址;
new.cpp
#include<iostream>
usingnamespace std;
int main (void){
// int* pi = (int*)malloc (sizeof (int));
// free (pi);
int* pi =newint;
*pi =1000;
cout <<*pi << endl;
delete pi;
pi = NULL;
/*
*pi = 2000;
cout << *pi << endl;
*/
pi =newint[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 =newint(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]=newint[3][4];
delete[] p;
int(*q)[4][5]=newint[3][4][5];
delete[] q;
return0;
}
![](https://img2018.cnblogs.com/blog/1167156/201811/1167156-20181129225746583-1803846900.png)
new/delete:对单个变量进行内存分配/释放;
new[]/delete[]:对数组进行内存分配/释放;
new int[3][4] 返回的是一个二维数组第一个元素的地址;
三维数组:二维数组的数组
int (*p) [4] = new int [3][4];
用二维数组的指针去接受三维数组;
动态内存分配:
string自动维护内存,无内存限制; “\0”,且自动添加 ‘\0’
对单个变量:
![](https://img2018.cnblogs.com/blog/1167156/201811/1167156-20181129225746893-858658025.png)
对数组:
![](https://img2018.cnblogs.com/blog/1167156/201811/1167156-20181129225747162-628779353.png)
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
#include<iostream>
usingnamespace 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;
return0;
}
retref.cpp
#include<iostream>
usingnamespace 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
return0;
}
int b = 10;
int a = func(b); 返回右值;
func(b) = a; 返回左值;
foo ( ) = 200; //200赋给临时变量;
int & foo (void) {
return g_data;
}
// 200赋给g_data了,此时赋的是别名int & foo (void);
static.cpp
#include<iostream>
usingnamespace std;
int main (void){
int* p1 = NULL;
void* p2 = p1;
p1 =static_cast<int*>(p2);
return0;
}
指针p2进行静态类型转换:
p1 = static_cast<int*> (p2);
![](https://img2018.cnblogs.com/blog/1167156/201811/1167156-20181129225749678-1267068364.png)
string.cpp
#include<iostream>
#include<cstring>
#include<cstdio>
usingnamespace 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);
return0;
}
![](https://img2018.cnblogs.com/blog/1167156/201811/1167156-20181129225750249-1539700701.png)
cout <<(strcasecmp (s1.c_str (),
s3.c_str ())==0)<< endl;
不区分大小写进行比较,因为要求用char类型,所以用s1.c_str()和s3.c_str()
student.cpp
#include<iostream>
usingnamespace std;
classStudent{
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 ("包子");
return0;
}
swap.cpp
#include<iostream>
usingnamespace 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 (constchar* x,constchar* y){
constchar* z = x;
x = y;
y = z;
}
void swap5 (constchar** x,constchar** y){
constchar* z =*x;
*x =*y;
*y = z;
}
void swap6 (constchar*& x,constchar*& y){
constchar* z = x;
x = y;
y = z;
}
structStudent{
char name[1024];
int age;
};
void print (conststructStudent& s){
cout << s.name <<","<< s.age << endl;
// s.age = -1;
}
void foo (constint& 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
constchar* 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);
return0;
}
![](https://img2018.cnblogs.com/blog/1167156/201811/1167156-20181129225750910-882973491.png)
引用:
swap3 (a, b); -> void swap3 (int& a, int& b) {......}
指针:
void swap5 (constchar** x,constchar** y)
要用二级指针,即,const char ** x
引用型参数:
void print (conststructStudent& s)
&符号避免拷贝的开销;提高了效率;
避免拷贝,通过加const可以防止在函数中意外地修改实参的值,
同时还可以接受拥有常属性的实参。
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">