1.C++头文件不必是. h结尾
C语言中的标准库头文件如math. h,stdio.h 在C++被命名为cmath, cstdio.
#include <cmath>
#include <cstdio>
int main() {
double a = 1.2;
a=sin (a) ;
printf ("%lf\n", a) ;
}
2.名字空间namespace
为防止名字冲突(出现同名),C++引入了名字空间( namespace),
通过::运算符限定某个名字属于哪个名字空间
//如“计算机1702”::“李平”
//如“信计1603”::“李平”
namespace first {
int a;
void f() {/*...*}
int g() {/*...*}
}
namespace second {
double a;
double f() {/*...*/}
char g;
}
int main (){
first: :a= 2;
second: :a = 6.453;
first: :a = first::g()+second: :f();
second: :a = first : :g()+6.453;
printf ("%d\n",first::a);
printf("%lf\n", second: :a) ;
return 0;
}
通常有3种方法使用名字空间X的名字name::
-
using namespace X;//引入整个名字空间。引用后该区域内写X的函数都不需要加X::
-
using X: : name ; //使用单个名字。引用后该区域内写X的name函数不需要加X::
-
X: : name; //程序中加上名字空间前缀,如X::
3.C++的新的输入输出流库(头文件iostream)
将输入输出看成一个流,
并用输出运算符<<和输入运算符>>对数据(变量和常量进行输入输出);
- cout代表标准输出流对象(屏幕窗口)
- cin代表标准输入流对象(键盘);
所有标准库中的名字都属于标准名字空间std.
#include <iostream>
using std: :cout; //使用单个名字
int main() {
double a;
cout <<"从键盘输入一个数”<< std::endl; //lendl表示换行符,并强制输出
std: :cin >> a; //通过“名字限定”std: :cin,
//cin是代表键盘的输入流对象,>>等待键盘输入一个实数
a= sin(a);
cout << a; //cout是代表屏幕窗口的输出流对象
return 0;
}
#include <iostream> //标准输入输出头文件
#include <cmath>
using namespace std; //引入整个名字空间std中的所有名字
//cout cin都属于名字空间std;
int main() {
double a;
cout <<"从键盘输入一个数"<< endl;
cin >> a;
a = sin(a);
cout << a;
return 0;
}c
4.变量定义
c++变量“即用即定义”,且可用表达式初始化
#include <iostream>
using namespace std;
int main () {
double a = 12*3.25;
double b = a + 1.112;
cout << "a contains : " << a << endl;
cout << "b contains: " << b << endl;
a =a *2+ b;
double c = a + b *a; //“即用即定义”,且可用表达式初始化
cout<< "c contains: " <<c<< endl;
5.程序块{}内部作用域可定义域外部作用域同名的变量,在该块里就隐藏了外部变量
#include <iostream>
using namespace std;
int main () {
double a;
cout <<_"Type a number: ";
cin >> a;
{
int a = 1; // "int a"隐藏了外部作用域的“double a"
a = a* 10 +4;
cout << "Local number: " << a << endl;
}
cout <<"You typed: " <<a << endl; //main作用域的“double a"
6.for循环语句可以定义局部变量。
using namespace std;
int main() {
int i = 0;
for (int i = 0; i < 4; i++) {
cout << i << endl;
}
cout << "i contains: "<< i << endl;
for (i = 0; i< 4; i++) {
for (int i = 0; i < 4; i++){ // we're between
//previous fors hooks
cout << i<<" ";
}
cout << endl;
}
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tysI4woU-1659583994569)(C:\Users\ZHAI\AppData\Roaming\Typora\typora-user-images\image-20220628195715626.png)]
7.访问和内部作用域变量同名的全局变量,要用全局作用域限定::
#include <iostream>
using namespace std;
double = 128;
int main () {
double a = 256;
cout << "Local a: " << a << endl;
cout <<"Global a: " <<::a << endl; //::是全局作用域限定
return 0;
}
8.C++引入了“引用类型”,即一个变量是另一个变量的别名
#include <iostream>
using namespace std;
int main () {
double a = 3.1415927;
double &b = a; // b 是 a 的别名,b就是a
b = 89; //也就是a的内存块值为89
cout << "a contains: " << a << endl; // Displays 89
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-orGIeyLe-1659583994572)(C:\Users\ZHAI\AppData\Roaming\Typora\typora-user-images\image-20220628200430063.png)]
引用经常用作函数的形参,表示形参和实参实际上是同一个对象,在函数中对形参的修改也就是对实参的修改
#include <iostream>
using namespace std;
void swap(int x,int y) {
cout << "swap函数内交换前:"<< x <<""<< y << endl;
int t = x; X = y; y = t;
cout << "swap函数内交换后:"<< x <<" "<< y << endl;
}
int main() {
int a = 3, b = 4;
swap(a,b);
cout << a << "," << b << endl; // Displays 100,4.
//a,b的值并未真正发生改变
return 0;
}
/*
x, y得到2个int型变量的指针,x, y本身没有修改
修改的是x,y指向的那2个int型变量的内容
*/
void swap(int *,int *y) {
cout << "swap函数内交换前:" << *x << " " << *y << endl;
int t =*x; *x=*y; *y = t;
cout << "swap函数内交换后:" << *x << " " << *y << endl;
}
int main() {
int a = 3,b = 4;
swap(&a,&b); // &a赋值给x,&b赋值给y,
//x, y分别是int*指针,指向a, b
//*x,*y就是a和b
cout << a << ", "<< b << endl;
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-McfLVZtm-1659583994573)(C:\Users\ZHAI\AppData\Roaming\Typora\typora-user-images\image-20220628201850142.png)]
//x, y是实参的引用
void swap(int &x,int &y) {
cout <<"swap函数内交换前: "<<x <<" " << y <<、endl;
int t = x; X =y; y = t;
cout << "swap函数内交换后:"<< x <<" "<<y << endl;
}
int main() {
int a = 3, b = 4;
swap(a,b);//x, y将分别是a, b的引用,即x就是a, y就是b
cout << a<< "," << b << endl; // Displays 3,4.
return 0;
}
当实参占据内存大时,用引用代替传值(需要复制)可提高效率,
如果不希望因此无意中修改实参,可以用const修改符。如
#include <iostream>
using namespace std;
void change (double &x,const double &y, double z) {
x =100;
y = 200; //error!会报错 常量y不可修改,是const double &
z= 300;
}
int main () {
double a, b,c;//内在类型变量未提供初始化式,默认初始化为0
change(a,b,C);n h< ". "<<c << endl;
return 0;
}
9.inline内联函数
对于不包含循环的简单函数,建议用inline关键字声明为"inline内联函数",
调用时编译器将内联函数调用用其代码展开直接替换,称为“内联展开”,避免函数调用开销,提高程序执行效率
#include <iostream>
#include <cmath>
using namespace std;
inline double distance(double a,double b) {
return sqrt(a* a + b * b);
}
int main() {
double k = 6, m = 9;
//下面2行将产生同样的代码:
cout << distance (k,m) << endl;
cout << sqrt(k * k + m* m) << endl; //两者效率相同
return 0;
}
10.异常处理机制(同java)
通过try-catch处理异常情况
正常代码放在try块,catch中捕获try块抛出的异常
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
int main () {
int a,b;
cout <<"Type a number: ";
cin >> a;
cout << endl;
try {
if (a > 100)throw 100;
if (a< 10)throw 10;
throw a / 3;
}
catch (int result) {
cout <<"Result is: " <<result << endl;
b = result + 1;
cout << "b contains: " << b << endl;
}
catch (char *s){
cout << "haha" << string(s) << endl;
}
cout << endl;
char zero[] = "zero";
char pair[] = "pair";
char notprime[] = "not prime";
char prime[] = "prime";
try {
if (a == 0) throw zero;
if ((a / 2)*2== a) throw pair;
for (int i = 3; i <= sqrt (a); i++){
if ((a / i) * i == a) throw notprime;
}
throw prime;
}
catch (char *conclusion) {
cout << "异常结果是:" << conclusion << endl;
}
catch(...) {
cout << "其他异常情况都在这里捕获" << endl;
}
cout << endl;
return 0;
}
11.默认形参
:函数的形参可带有默认值。必须一律在最右边
#include <iostream>
using namespace std;
double test(double a,double b = 7) {
return a - b;
}
int main() {
cout << test (14,5)<< endl;
cout << test(14)<< endl;
return 0;
}
/*错:默认参数一律靠右*/
double test (double a,double b = 7,int c) {
return a - b;
}
12.函数重载
C++允许函数同名,只要它们的形参不一样(个数或对应参数类型),
调用函数时将根据实参和形参的匹配选择最佳函数,
如果有多个难以区分的最佳函数,则变化一起报错! 注意:不能根据返回类型区分同名函数
#include <iostream>
using namespace std;
double add(double a,double b){
return a + b;
}
int add(int a,int b){
return a + b;
}
//错:编译器无法区分int add (int a, int b), void add (int a,int b)
void add(int a,int b) {
return a + b;
}
int main() {
double m= 7, n = 4;
int k = 5,p = 3;
cout << add(m,n)<<", " << add(k,p)<< endl;
return 0;
}
13.运算符重载
#include <iostream>
using namespace std;
struct Vector2 {
double x;
double y;
};
Vector2 operator *(double a,Vector2 b) {
Vector2 r;
r.x = a*b.x;
r.y = a*b.y;
return r;
}
Vector2 operator+ (Vector2 a,Vector2 b){
Vector2 r;
r.x = a.x +b.x;
r.y = a.y + b.y;
return r;
}
int main() {
Vector2 k,m; //C++定义的struct类型前不需要再加关键字struct: "struct"
k.x = 2; //用成员访问运算符.访问成员
k.y = -1;
m = 3.1415927 *k;
cout << "("<< m.x << "," << m.y <<")"<< endl;
Vector2 n = m + k;
cout << "(" << n.x << "," << n.y << ")" << endl;
return 0;
}
系统方法重载
#include <iostream>
using namespace std;
struct vector{
double x;
double y;
};
ostream& operator << (ostream& o,vector a){
o << " ("<<a.x << ","<< a.y <<")";
return 0;
}
int main (){
vector a;
a.x =35;
a.y = 23;
cout << a << endl; // operator <<(cout, a) ;
return 0;
}
14.模板template函数
:厌倦了对每种类型求最小值
#include <iostream>
using namespace std;
int minValue(int a,int b) { //return a<b?a:b
if (a< b) return a;
else return b;
}
double minValue(double a,double b) { //return a<b?a:b
if (a < b) return a;
else return b;
}
int main() {
int i = 3,j = 4;
cout << "min of " << i << " and " << j << " is " << minValue(i,j) << endl;
double x = 3.5,y = 10;
cout << "min of " << x << " and " << y << " is " << minValue(x,y) << endl;
}
//可以转化成:模板函数-----类似java的泛型
#include <iostream>
using namespace std;
/**
可以对任何能比较大小(<)的类型使用该模板让编译器
自动生成一个针对该数据类型的具体函数
**/
template<class TT>
TT minValue(TT a,TT b){ //return a<b?a:b
if (a < b) return a;
else return b;
}
int main(){
int i = 3,j = 4;
cout << "min of " <<i << " and " << j << " is " << minValue(i,j) << endl;
double x = 3.5,y = 10;
cout << "min of " << x << " and " << y <<" is " << minValue (x, y)<< endl;
//但是,不同类型的怎么办?如下面I是整形而Y是浮点数
cout << "min of " << i < " and " << y << " is " << minValue(i, y)<< endl;
/**
template<class T1,class T2>
T1 minValue(T1 a,T2 b) {
if (a< b) return a;
else return (T1)b;
}
**/
}
15.动态内存分配
关键字 new 和 delete 比C语言的malloc/alloc/realloc和free更好,
new、delete只能在C++使用,而malloc、free只能在C中可以使用
可以对类对象调用初始化构造函数或销毁析构函数
// 堆程序区
#define _CRT_SECURE_NO_WARNINGS // windows
#include <iostream>
#include <cstring>
using namespace std;
int main() {
double d = 3.14; // 变量d是一块存放double值的内存块
double *dp; // 指针变量dp:保存double类型的地址的变量
// dp的值得类型是double *
// dp是存放double*类型值的内存块
dp = &d; // 取地址运算符&用于获得一个变量的地址,
// 将double变量d的地址(指针)保存到double*指针变量dp中
// dp和&d的类型都是double *
*dp = 4.14; // 解引用运算符*用于获得指针变量指向的那个变量(C++中也称为对像)
// *dp就是dp指向的那个d
cout<<"*dp= "<<*dp <<"d=:"<<d << endl:
cout <<"Type a number: ";
cin >> *dp; // 输出dp指向的double内存块的值
cout << "*dp= "<<*dp << "d=:"<< d << endl;
dp = new double; // new分配正好容纳double值的内存块(如4或8个字节)
// 并返回这个内存块的地址,而且地址的类型是double *
// 这个地址被保存在dp中,dp指向这个新内存块,不再是原来的
// 但目前这个内存块的值是未知的
// 注意:
// new分配的是堆存储空间,即所有程序共同拥有的自由内存
// 而d, dp等局部变量是这个程序自身的静态存储空间
// new会对这个double元素调用double类型的构造函数做初始
*dp = 45.3; //*dp指向的double内存块的值变成45.3
cout<< "Type a number:"
cin >> *dp; // 输出dp指向的double内存块的值
cout<< "*dp= " <<*dp<< endl:
*dp = *dp +5; //修改dp指向的double内存块的值45.3+5
cout<< "*dp= "<< *dp << endl;
delete dp; // delete释放dp指向的动态分配的double内存块
dp = new double[5]; // new分配了可以存放5个double值的内存块,
// 返回这块连续内存的起始地址,而且指针类型是
// double *,实际是第一个double元素的地址
// new会对每个double元素调用double类型的构造函数
dp[0] = 4456; // dp[o]等价于*(dp+0)即*dp,也即是第1个double元素
dp[1] = dp[0] + 567; // dp[1]等价于*(dp+1),也即是第2个double元素的
cout << "d[0]=: " << dp[0] << "d[1]=: " << dp[1] << endl;
delete[] dp; // 释放dp指向的多个double元素占据的内存块,
// 对每个double元素调用析构函数以释放资源
// 缺少门,只释放第一个double元素的内存块,
int n = 8;
dp = new double[n]; // new可以分配随机大小的double元素,
// 而静态数组则必须是编译期固定大小,
// 如double arr[2o];
// 通过下标访问每个元素
for (int i = 0; i < n; i++) {
dp[i] = i;
} // 通过指针访问每个元素
double *p = dp;
for (int i = 0; i < n; i++) {
cout << *(p + i) << endl; // p[i]或dp[i]
}
cout << endl;
for (double *p = dp,*q = dp + n; p < q; p++) {
cout<<*p << endl;
}
cout << endl;
delete[] dp;
char *s;
s = new char[100];
strcpy(s,"Hello!"); // 将字符串常量拷贝到s指向的字符数组内存块中
cout << s << endl;
delete[] s; // 用完以后,记得释放内存块,否则会“内存泄漏”!
return 0;
}
16.类
是在C的struct类型上,增加了“成员函数”。
C的strcut可将一个概念或实体的所有属性组合在一起,
类成员默认为private的,struct成员默认为public;
描述同一类对象的共同属C++使得struct不但包含数据,还包含函数(方法)用于访问或修改类变量(对象)的属性。
#include <iostream>
using name space std;
struct Date {
int d,m,y ;
void init(int dd,int mm,int yy) {
d = dd; m = mm; y = yy ;
}
void print() {
cout << y << "-"<< m << "-"<< d << end1;
}
int main () {
Date day;
day. print(); // 通过类Date对象day调用类Date的print方法
day.init(4,6,1999); // 通过类Date对象day调用类Date的init方法
day.print(); // 通过类Date对象day调用类Date的print方法
}
//成员函数返回“自引用” (*this)
#include <iostream>
using name space std ;
struct Date {
int d, m,y ;
void init(int dd,int mm,int yy) {
d = dd; m = mm; y = yy ;
}
void print() {
cout<< y << "-" <<m<< "-"<< d << endl;
}
Date& add(int dd){
d = d + dd;
return *this; // this是指向调用这个函数的类型对象指针,
// *this就是调用这个函数的那个对象
// 这个成员函数返回的是“自引用”,即调用这个函象本身
// 通过返回自引用,可以连续调用这个函数
// day. add(3);
// day. add (3 ).add (7) ;
int main() {
Date day;
day. print() ; // 通过类Date对象day调用类Date的print方法
day.init(4,6,1999); // 通过类Date对象day调用类Date的init方法
day. print(); // 通过类Date对象day调用类Date的print方法
day.add(3);
day.add (5).add (7);
day.print();
return 0;
}
//成员函数重载“运算符函数”#include <iostream>
using name space std;
struct Date {
int d, m,y ;
void init(int dd,int mm,int yy) {
d = dd; m = mm; y = yy ;
}
void print() {
cout << y << "-"<< m<< "-"<< d << endl;
}
Date& operator+=(int dd){
d = d + dd;
return *this; // this是指向调用这个函数的类型对象指针,
// *this就是调用这个函数的那个对象
// 这个成员函数返回的是“自引用”,即调用这个函象本身
// 通过返回自引用,可以连续调用这个函数
// day.add (3);
// day.add (3) .add(7);
}
};
int main() {
Date day;
day.print(); // 通过类Date对象day调用类Date的print方法
day.init(4,6,1999); // 通过类Date对象day调用类Date的init方法
day. print() ; // 通过类Date对象day调用类Date的print方法
day += 3; // day.add (3);
(day += 5) += 7; // day.add(5).add(7);
day. print();
return 0;
}
17.构造函数与析构函数
与类名同名函数为构造函数,可为类初始化,参数不同构造函数不同。
#include <iostream>
using namespace std; // 构造函数
struct Date {
int d, m,y;
Date() {
d = 1; m = 1; y = 1000 ;
std: :cout << "default constructor!"<< std: :endl
}
Date(int dd){
d = dd; m = 1; y = 1000;
std: : cout << "default constructor!"<< std: : endl;
}
Date(int dd, int mm){
d = dd; m = mm; y = 1000;
std: : cout << "default constructor!"<< std: : endl;
}
Date(int dd,int mm,int yy){
d = dd; m = mm; y = yy;
std: :cout << "constructor!"<< std: : endl;
}
/**
*可以三个简化成一个,只需要给参数赋默认值
Date(int dd=1,int mm=1,int yy=1000){
d = dd; m = mm; y = yy;
std : : cout << "constructor!"<< std: :endl;
}
**/
void print (){
cout << y << "-" << m << "-" << d << endl;
}
};
int main() {
Date day;
Date day1(4);
Date day2(4,6);
Date day3(4,6,1999); // day.d day.m day. y
// day. print() ; // 通过类Date对象day调用类Date的print方法
// day.init(4,6,1999); // 通过类Date对象day调用类Date的init方法
day. print () ; // 通过类Date对象day调用类Date的print方法
day2. print () ;
return 0;
}
带字符串的结构体构造方法不同
析构函数(不需要带参数)销毁时自动调用,释放占用内存
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;
class student {
private: // class定义的类的成员默认都是私有的private,外部函数无法通过类对象成员或类成员函数
char *name;
int age;
public: /** 接口:
*public的公开成员(一般是成员函数)称为这个类的对外接口,外部函数只能通过这些接口访问类对
*private等非public的包含内部内部细节,不对外公开,从而可以封装保护类对象!
**/
char *get_name() { return name; }
int get_agg() { return age; }
void set_name(char *n = "new_name") {
delete[] name;
int len = strlen(n);
name = new char[len+1];
strcpy (name,n);
}
void set_age(int new_age) { age = new_age; }
student(char *n = "no_name", int a = 15){
int len = strlen(n);
name = new char[len+1];
strcpy (name,n);
age = a;
}
//析构函数格式
virtual ~student() {
delete[] name;
std::cout << "destructor!" << name << std::endl;
}
};
void f() {
student stu1;
student stu2("wang");
student stu3("zhang",23);
std::cout<< stu1.get_name() << "\t" << stu1.get_age() << std::end1;
std::cout<< stu2.get_name() << "\t" << stu2.get_age() << std::end1;
std::cout<< stu3.get_name() << "\t" << stu3.get_age() << std::end1;
stu1.set_name("li");
stu1.set_age("12");
}
int main() {
f();
}
18.拷贝
拷贝构造函数、赋值运算符
下列赋值为什么会出错?
“ student m(s);
s = k ;”
拷贝构造函数:定义一个类对象时用同类型的另外对象初始化
赋值运算符:一个对象赋值给另外一个对象
#define_CRT_SECURE_NO_WARNINGS // windows系统
#include <iostream>
#include <cstdlib>
using namespace std;
struct student {
char *name;
int age;
student(char *n = "no name", int a = 0){
name = new char[100];
strcpy (name,n);
age = a;
cout <<"构造函数,申请了100个char元素的动态空间"<< endl;
}
student (const student &s) {
name = new char[100];
strcpy(name,s.name); //两者不共用一个内存
age = s.age;
cout<<"拷贝构造函数"<< endl;
}
student& operator=(const student &s) {
name = new char[100];
strcpy(name,s.name); //构造拷贝运算符
age = s.age;
cout <<"拷贝构造函数"<< endl;
return *this;
}
virtual ~student() { // 析构函数
delete[] name; // 不能用free!
cout <<"析构函数,释放了100个char元素的动态空间"<< endl;
}
};
int main() {
student s;
student k("John",56);
cout << k.name << ",age " << k.age << endl << endl;
student m(s); // 拷贝构造函数——把s的数值拷贝到m中,硬拷贝--两者共用一个内存空间
cout << s.name << ", age " << s.age << endl << endl;
cout << m.name << ", age " << m.age << endl << endl;
return 0;
}
19.类体外定义方法(成员函数)
必须在类定义中声明,类体外要有类作用域,否则就是全局外部函数了!
#include <iostream>
using namespace std;
class Date {
int d,m,y;
public:
void print() ;
Date(int dd = 1, int mm= 1, int yy = 1999){
d = dd; m = mm; y = yy;
cout<<"构造函数"<< endl;
}
~Date(){ // 析构函数名是~和类名,且不带参数,没有返回类型
//目前不需要做任何释放工作,因为构造函数没申请资源
cout <<"析构函数"<< endl;
}
};
void Date: : print() {
cout << y << "-"<< m<< "-" << d << endl;
}
int main() {
Data day;
day.print();
}
20.类模板(泛型)
我们可以将一个类变成“类模板”或“模板类”,正如一个模板函数一样。
#include <iostream>
#include <cstdlib>
using namespace std;
template<class T> //说明模板T
class Array {
T size;
T *data;
public:
Array(int s){
size = s;
data = new T[s];
}
~Array() {
delete[] data;
}
T &operator [] (int i) {
if (i < o || i >= size){
cerr << end1 << "out of bounds"<< endl;
throw "下标超出范围"
exit(EXIT_FAILURE);
}
else return data[i];
}
};
int main() {
int n;
cin >> n;
Array<int> t(n);
t[0] = 45; // OK
t[4] = t[0] +6; // OK
cout << t[4]<< endl; // OK
t[10] = 7; // error!
return 0;
}
21.typedef类型别名
#include <iostream>
using namespace std;
typedef int INT;
int main() {
INT i = 3: // 等价于int i = 3;
cout << i << endl;
return 0;
}
22.string
#include <iostream>
#include <string> // typedef std: : basic_string<char> string;
using namespace std;
typedef string String;
int main() {
// with no arguments
string sl; // 默认构造函数:没有参数或参数有默认值
String s2("hello"); // 普通构造函数 String就是string
sl - "Anatoliy" : // 赋值运算符
String s3(s1); // 拷贝构造函数
cout << "s1 is: " << s1 << endl;
cout << "s2 is: " << s2 << endl;
cout << "s3 is: " << s2 << endl;
// first argumen C string
// second number of characters
string s4("this is a C_sting",10); //从this is a C_sting里取前十个作为值
cout << "s4 is: " << s4 << endl;
// 1 -C++ string
// 2 - start position
// 3 - number of characters
string s5(s4,6,4); // 从s4字符串中的第六个位置去4个字母作为字符串
cout << "s5is: " << s5 << endl;
// 1 - number characters
// 2 - character itself
string s6(15,'*'); //由15个*作为值
cout << "s6is: " << s6 << endl ;
// 1 - start iterator
// 2 - end iterator
string s7 = (s4.begin(),s4.end() - 5);
cout << "s7 is: " << s7 << endl;
// you can instantiate string with assignment
string s8 = "Anatoliy" ;
cout << "s8 is: " << s8 << endl;
string s9 = sl + "hello"+ s2; // sl +"hello"+ s2的结果是string类型的对象(变量)
cout<< "s9is: " << s9 << endl ;
return 0;
//遍历字符串访问其中元素
#include <iostream>
#include <string>
using namespace std;
int main() {
string s = "hell";
string w = "worl!";
s = s + w; // s += w ;
for (int ii - 0; ii != s.size(): ii++)
cout << ii <<"" << s[ii] << endl;
cout << endl;
string::const_iterator cii; // 迭代器 显示为下标的位置,也是对应位置的指针
// const_iterator只能访问不可修改
// string::iterator 普通迭代器可修改
int ii = 0;
for (cii = s.begin(); cii !- s.end(); cii++)
cout << ii++ << ” " << *cii << endl;
}
23.向量vector
表示一组可以是任何类型的向量
#include <vector>
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
using std::vector;
int main() {
vector<double> student_marks ;
// no size specified: vector containsl
// no element.s
int num_students ;
cout << "Number of students: " << endl ;
cin >> num students ;
student_marks.resize(num_students);
for (vector<double>::size_type i = 0; i < num_students; i++) {
cout << "Enter marks for student #"<<i + 1<<": " << endl ;
cin >> student_marks[i;
}
cout << endl;
for (vector<double>::iterator it = student_marks.begin(); it != student_marks.end(); it++){
cout<<*it<< endl ;
}
return 0;
}
25.Inheritance继承(Derivation派生)
继承性是程序设计中的一个非常有用的、有力的特性, 它可以让程序员在既有类的基础上,通过增加少量代码或修改少量代码的方法得到新的类, 从而较好地解决了代码重用的问题。
一个派生类(derived class)
从1个或多个父类(parent class)/基类(base class)继承,即继承父类的属性和行为,但也有自己的特有属性和行为。如:
// 定义一个基类( person 类)
class person {
private :
char name [10] ;
int age;
char sex;
public:
// …
} ;
// 定义一个派生类
class employee∶public person {
char department[20] ;
float salary;
public:
// …
} ;
派生方式”可以是关键字 private 或 public。如果使用了 private, 则称派生类从基类私有派生; 如果使用了 public,则称派生类从基类公有派生。派生方式可以缺省, 这时派生方式默认为private ,即私有派生。
在类名 employee 的冒号之后, 跟着关键字 public 与类名 person,这就意味着类 employee 将继承类 person 的全部特性。
关键字 public 指出派生的方式,告诉编译程序派生类employee从基类 person 公有派生。
声明一个派生类的一般格式为:
class 派生类名∶派生方式 基类名 {
// 派生类新增的数据成员和成员函数
} ;
- 无论哪种派生方式, 基类中的私有成员既不允许外部函数访问, 也不允许派生类中的成员函数访问,但是可以通过基类提供的公有成员函数访问。
- 公有派生与私有派生的不同点在于基类中的公有成员在派生类中的访问属性。
公有派生时, 基类中的所有公有成员在派生类中也都是公有的。
私有派生时, 基类中的所有公有成员只能成为派生类中的私有成员。
1.私有派生
(1 ) 私有派生类对基类成员的访问
由私有派生得到的派生类, 对它的基类的公有成员只能是私有继承。也就是说基类的所有公有成员都只能成为私有派生类的私有成员, 这些私有成员能够被派生类的成员函数访问,但是基类私有成员不能被派生类成员函数访问。
# include <iostream>
using namespace std;
class base { // 声明一个基类
int x;
public:
void set_x(int n) {
x = n;
}
void show_x () {
cout << x << endl;
}
};
class derived: private base { // 声明一个私有派生类
int y;
public:
void set_xy(int n, int m) {
set_x( n ) ; // 基类的共有成员变为派生类的私有成员,此时调用时合法的
y = m;
}
void show_xy() {
cout<< x << y << endl; // 直接引用基类的的私有数据是非法的
}
/**
*void showxy( ) {
* showx( );
* cout < < y < < endl;
*}
*改成上述代码才能跑
**/
};
int main() {
derived obj;
obj.set_xy( 10,20) ;
obj.show_xy() ;
return 0 ;
}
(2 ) 外部函数对私有派生类继承来的成员的访问
私有派生时,基类的公有成员在派生类中都成为私有成员, 外部函数不能访问。下面的例子将验证外部函数对私有派生类继承来的成员的访问性。
#include <iostream>
using namespace std;
class base {
// 声明一个基类
int x;
public:
void set_x(int n) {
x = n;
}
void show_x () {
cout << x << endl;
}
};
class derived: private base {
// 声明一个私有派生类
int y;
public:
void sety(int n) {
y = n;
}
void showy() {
cout << y << endl;
}
};
int main() {
derived obj;
obj .set_ x(10) ; // 私有派生类外部调用基类的共有类 非法!
obj .sety(20) ; // 合法
obj .show_x() ; // 非法
obj .showy() ; // 合法
return 0 ;
}
基类共有成员方法成为 derived 的私 有成员后, 只能 被derived的成员函数访问, 不能被外界函数访问。
但是因为仍为base的共有成员,下列调用合法
base base-obj;
base-obj.setx (2);
2.公有派生
在公有派生中,基类中的私有成员不允许外部函数和派生类中的成员函数直接访问,但是可以通过基类提供的公有成员函数访问。基类中的公有成员在派生类中仍是公有成员,外部函数和派生类中的成员函数可直接访问。
#include <iostream>
using namespace std;
class base {
// 声明一个基类
int x;
public:
void set_x(int n) {
x = n;
}
void show_x() {
cout << x << endl;
}
};
class derived: public base {
// 声明一个公有派生类
int y;
public:
void set_y(int n) {
y = n;
}
void show_y() {
cout << y << endl;
}
} ;
int main ()
{
derived obj;
obj.set_x(10); // 合法
obj.set_y(20); // 合法
obj.show_x(); // 合法
obj.show_y(); // 合法
return 0 ;
}
- 派生类以公有派生的方式继承了基类, 并不意味着派生类可以访问基类的私有成员。
派生类 derived 企图访问基类 base 的私有成员 x, 但是这种企图是非法的,因为基类无论怎样被继承, 它的私有成员都针对该基类保持私有性。
2.在派生类中声明的名字支配基类中声明的同名的名字, 即如果在派生类的成员函数中直接使用该名字的话,则表示使用派生类中声明的名字
class X
{
public:
int f() ;
};
class Y∶public X
{
public:
int f();
int g() ;
};
void Y∷g()
{
f() ; // 表示被调用的函数是 Y∷f( ), 而不是 X∷f( )
}
对于派生类的对象的引用,也有相同的结论, 例如:
Y obj;
obj .f( ) ; // 被调用的函数是 Y∷f( )
//如果要使用基类中声明的名字,则应使用作用域运算符限定, 例如:
obj .X∷f( ) ; // 被调用的函数是 X∷f( )
protected
protected 说明符可以放在类声明的任何地方,通常将它放在私有成员声明之后, 公有成员声明之前。类声明的一般格式如下所示:
class 类名
{
[private:]
私有数据成员和成员函数
protected:
保护数据成员和成员函数
public:
公有数据成员和成员函数
};
保护成员可以被派生类的成员函数访问,但是对于外界是隐藏起来的, 外部函数不能访问它。因此,为了便于派生类的访问, 可以将基类私有成员中需要提供给派生类访问的成员定义为保护成员。
派生类的构造函数和析构函数
1.派生类构造函数和析构函数的执行顺序
通常情况下,当创建派生类对象时, 首先执行基类的构造函数, 随后再执行派生类的构造函数; 当撤消派生类对象时, 则先执行派生类的析构函数, 随后再执行基类的析构函数。
#include<iostream>
using namespace std;
class base
{
public:
base()
{
cout<< "Constructing base class \n";
} // 基类的构造函数
~base()
{
cout <<"Destructing baes class \n";
} // 基类的析构函数
} ;
class derive : public base
{
public:
derive() // 派生类的构造函数
{
cout << "Constructing derived class \n";
}
~derive() // 派生类的析构函数
{
cout<< "Destructing derived class \n";
}
};
int main()
{
derive op;
return 0 ;
}
程序运行结果:
Constructing base class
Constructing derived class
Destructing derived class
Destructing base class
构造函数的调用严格地按照先调用基类的构造函数, 后调用派生类的构造函数的顺序执行。析构函数的调用顺序与构造函数的调用顺序正好相反,先调用派生类的析构函数, 后调用基类的析构函数。
2.派生类构造函数和析构函数的构造规则
当基类的构造函数没有参数,或没有显式定义构造函数时, 派生类可以不向基类传递参数,甚至可以不定义构造函数。
派生类不能继承基类中的构造函数和析构函数。当基类含有带参数的构造函数时,派生类必须定义构造函数,甚至所定义的派生类构造函数的函数体可能为空,仅仅起参数的传递作用。以提供把参数传递给基类构造函数的途径。
派生类构造函数名(参数表) :基类构造函数名(参数表)
{
// …
}
若基类使用缺省构造函数或不带参数的构造函数, 则在派生类中定义构造函数时可略去“∶基类构造函数名(参数表)”; 此时若派生类也不需要构造函数, 则可不定义构造函数。
下面的程序说明如何传递一个参数给派生类的构造函数和传递一个参数给基类的构造函数。
#include<iostream>
using namespace std;
class base {
int i;
public:
base(int n) {// 基类的构造函数
cout << "Constructing base class \n";
i = n;
}
~base () {// 基类的析构函数
cout << "Destructing base class \n";
}
void show_i() {
cout << i << endl;
}
};
class derive : public base {
int j;
public:
derive(int n, int m) : base (m) { // 定义派生类构造函数时,
// 缀上基类的构造函数
cout << "Constructing derived class"<< endl;
j = n;
}
~derive() { // 派生类的析构函数
cout << "Destructing derived class"<< endl;
}
void show_j() {
cout << j << endl;
}
};
int main() {
derive obj(30, 40) ;
obj.show_i();
obj.show_j();
return 0 ;
}
基类的析构函数不会因为派生类没有析构函数而得不到执行, 它们各自是独立的。
在定义派生类对象时,构造函数的执行顺序如下:
- 基类的构造函数
- 对象成员的构造函数
- 派生类的构造函数
撤消对象时,析构函数的调用顺序与构造函数的调用顺序正好相反。
#include<iostream>
using namespace std;
class base {
int x;
public:
base(int i) { // 基类的构造函数
x = i;
cout << "Constructing base class \n";
}
~base() {} // 基类的析构函数
cout << "Destructing base class \n";
}
void show() {
cout << "x = "<< x << endl;
}
} ;
class derived: public base {
base d;
// d 为基类对象,作为派生类的对象成员
public:
derived(int i) : base (i), d(i) { // 派生类的构造函数, 缀上基类构造函数和
// 对象成员构造函数
cout << "Constructing derived class \n";
}
~derived() // 派生类的析构函数
{ cout << "Destructing derived class \n"; }
} ;
int main () {
derived obj( 5 );
obj.show();
return 0 ;
}
程序执行结果:
Constructing base class
Constructing base class
Constructing derived class
x = 5
Destructing derived class
Destructing base class
Destructing base class
26.虚函数Virtual Functions
派生类的指针可以自动转化为基类指针,可以用基类数组存储派生类。
//经理也是雇员
Employee* employees[100];
int e_num=0;
int main() {
Employee* employees[100]; // 基类的数组
int e_num = 0;
Employee* p; // 基类的指针
string name;
int level;
int cmd;
while (cin >> cmd) {
if (cmd == 'M' || cmd == 'm') {
cout << "请输入和级别" << endl;
cin >> name >> level;
p = new Manager (name, level);
employees[e_num] = p;
e_num++;
}
else {
cout << "请输入姓名" << endl ;
cin >> name;
p = new Employee (name);
employees[e_num] = p;
e_num++;
}
}
for (int i = 0; i < e_num; i++) {
employees[i]->print() ;
}
}
用一个指向基类的指针分别指向基类对象和派生类对象,并2次调用
class Employee {
string name;
public:
Employee(string n);
virtual void print(); // 需要函数有多态性就变为虚函数
};
class Manager : public Employee {
int level;
public:
Manager(string n,int l = 1);
virtual void print (); // 虚函数
};
Employee::Employee(string n) : name(n) { //初始化成员列表
//name = n;
}
void Employee::print() {
cout << name << endl;
}
Manager::Manager(string n,int l) :Employee(n),level(1){
}
void Manager::print() {
cout << level <<" \t";
Employee::print();
}
int main() {
Employee *p;
Manager m("Zhang",2);
Employee e("Li");
p = &e;
p->print();
p = &m;
p->print(); // 多态性 会指向自己的对应类型的成员方法而不是基类的
}
可以从一个类派生出多个不同的类
可以从多个不同的类派生出一个类来:多重派生(Multiple inheritance)
class One{
// class internals
}
class Two{
// class internals
}
class MultipleInheritance : public One, public Two {
// class internals
}
27.纯虚函数(pure virtual function )和抽象类(abstract base class)
函数体=0的虚函数称为“纯虚函数”。包含纯虚函数的类称为“抽象类”,通常作为接口
#include <string>
class Animal { // This Animal is an abstract base class
protected:
std::string m_name;
public:
Animal(std::string name)
: m_name(name)
{ }
std::string getName() { return m_name;}
virtual const char* speak() = 0; // note that speak is now a pure virtual function
};
int main() {
Animal a; //错:抽象类不能实例化(不能定义抽象类的对象(变量))
}
//从抽象类派生的类型如果没有继承实现所有的纯虚函数,则仍然是“抽象类”
28.常用函数
count_if
count_if()函数是对指定区域中符合指定条件计数的一个函数.
count_if()通过前两个参数指定区域,第三个参数是一个返回true或false的函数对象。函数count_if()计算这样的元素数,即它使得指定的函数对象返回true。
例如,计算容器numbers中有多少个数可以被3整除,这一这样编写:
bool fun3(int x) {return x % 3 == 0;}
int count3 = std::count_if(numbers.begin(), numbers.end(), func3);
//lambad函数式编程
map
map是STL的一个关联容器,它提供一对一的hash。
-
第一个可以称为关键字(key),每个关键字只能在map中出现一次;
-
第二个可能称为该关键字的值(value);
使用map
使用map得包含map类所在的头文件
#include <map> //注意,STL头文件没有扩展名.h
map对象是模板类,需要关键字和存储对象两个模板参数:
std:map<int, string> personnel;
map<int, string> mapStudent;
这样就定义了一个用int作为索引,并拥有相关联的指向string的指针.
插入元素
// 定义一个map对象
map<int, string> mapStudent;
// 第一种 用insert函數插入pair
mapStudent.insert(pair<int, string>(000, "student_zero"));
// 第二种 用insert函数插入value_type数据
mapStudent.insert(map<int, string>::value_type(001, "student_one"));
// 第三种 用"array"方式插入(好用)
mapStudent[123] = "student_first";
mapStudent[456] = "student_second";
查找元素
当所查找的关键key出现时,它返回数据所在对象的位置,如果沒有,返回iter与end函数的值相同。
// find 返回迭代器指向当前查找元素的位置否则返回map::end()位置
iter = mapStudent.find("123");
if(iter != mapStudent.end()) // 查找不能忘记这一步
cout<<"Find, the value is"<<iter->second<<endl; //iter->second查看value
else
cout<<"Do not Find"<<endl;
刪除与清空元素
//迭代器刪除;函数erase
iter = mapStudent.find("123");
mapStudent.erase(iter);
//用关键字刪除
int n = mapStudent.erase("123"); //如果刪除了會返回1,否則返回0
//用迭代器范围刪除 : 把整个map清空
mapStudent.erase(mapStudent.begin(), mapStudent.end());
//等同于mapStudent.clear()
map的大小
int nSize = mapStudent.size();
map的基本操作函数
begin() | 返回指向map头部的迭代器 |
---|---|
clear() | 删除所有元素 |
count() | 返回指定元素出现的次数, (帮助评论区理解: 因为key值不会重复,所以只能是1 or 0) |
empty() | 如果map为空则返回true |
end() | 返回指向map末尾的迭代器 |
equal_range() | 返回特殊条目的迭代器对 |
erase() | 删除一个元素 |
find() | 查找一个元素 |
et_allocator() | 返回map的配置器 |
insert() | 插入元素 |
key_comp() | 返回比较元素key的函数 |
max_size() | 返回可以容纳的最大元素个数 |
rbegin() | 返回一个指向map尾部的逆向迭代器 |
rend() | 返回一个指向map头部的逆向迭代器 |
size() | 返回map中元素的个数 |
swap() | 交换两个map |
upper_bound() | 返回键值>给定元素的第一个位置 |
value_comp() | 返回比较元素value的函数 |
";
### **查找元素**
当所查找的关键key出现时,它返回数据所在对象的位置,如果沒有,返回iter与end函数的值相同。
// find 返回迭代器指向当前查找元素的位置否则返回map::end()位置
iter = mapStudent.find(“123”);
if(iter != mapStudent.end()) // 查找不能忘记这一步
cout<<“Find, the value is”<second<<endl; //iter->second查看value
else
cout<<“Do not Find”<<endl;
### **刪除与清空元素**
//迭代器刪除;函数erase
iter = mapStudent.find(“123”);
mapStudent.erase(iter);
//用关键字刪除
int n = mapStudent.erase(“123”); //如果刪除了會返回1,否則返回0
//用迭代器范围刪除 : 把整个map清空
mapStudent.erase(mapStudent.begin(), mapStudent.end());
//等同于mapStudent.clear()
### **map的大小**
int nSize = mapStudent.size();
### **map的基本操作函数**
| begin() | 返回指向map头部的迭代器 |
| -------------- | ------------------------------------------------------------ |
| clear() | 删除所有元素 |
| count() | 返回指定元素出现的次数, (帮助评论区理解: 因为key值不会重复,所以只能是1 or 0) |
| empty() | 如果map为空则返回true |
| end() | 返回指向map末尾的迭代器 |
| equal_range() | 返回特殊条目的迭代器对 |
| erase() | 删除一个元素 |
| find() | 查找一个元素 |
| et_allocator() | 返回map的配置器 |
| insert() | 插入元素 |
| key_comp() | 返回比较元素key的函数 |
| max_size() | 返回可以容纳的最大元素个数 |
| rbegin() | 返回一个指向map尾部的逆向迭代器 |
| rend() | 返回一个指向map头部的逆向迭代器 |
| size() | 返回map中元素的个数 |
| swap() | 交换两个map |
| upper_bound() | 返回键值>给定元素的第一个位置 |
| value_comp() | 返回比较元素value的函数 |