cin.getline
char a[20];
cin.getline(a,5);
若输入超过5,输入流关闭,需要执行“cin.clear()”重新打开缓冲区。
cin.getline()实际上有三个参数,cin.getline(接收字符串的变量,接收字符的个数,结束字符)
当第三个参数省略时,系统默认为'\0' 。
当同时使用cin>>,getline()时,需要注意的是,在cin>>输入流完成之后,getline()之前,需要通过
str="\n";
getline(cin,str);
的方式将回车符作为输入流cin以清除缓存,如果不这样做的话,在控制台上就不会出现getline()的输入提示,而直接跳过,因为程序默认地将之前的变量作为输入流。
getline
#include<string>
string str;
getline(cin,str);
cin.get(a,10); //读取9个字符
cin.get() //去除结束符
get函数将结束符保留在输入流中
cin.get()用于舍弃输入流的不需要的字符(比如回车什么的)。
C++以十进制、八进制、十六进制输入输出:det、oct、hex
#include<iomanip>
setw()函数——当输出字段长度小于n的时候,在该字段前面用空格补齐;当输出字段长度大于n时,全部整体输出。(n包括要输出的字段)
setfill()函数——配合setw使用其他字符填充。
sizeof(数据类型/变量)——数据类型占用内存大小(字节)。
字符型变量对应ASCII码——char a; cout << (int)a;
#include<stream>
ifstream ifs;
ifs.open("test.txt",ios::in)
if(!ifs.is_open())——判断文件是否打开成功
读数据
char buf[1024] = { 0 };
while( ifs >> buf )
{
cout << buf << endl;
}
char buf[1024] = { 0 };
while( ifs.getline(buf, sizeof(buf)))
{
cout << buf << endl;
}
string buf;
while( getline(ifs, buf) )
{
cout << buf << endl;
}
char c;
while( c = (ifs.get()) != EOF )
{
cout << c;
}
ifs.close();
二进制写文件
class Person
{
public:
char m_Name[64];
int m_Age;
};
ofstream ofs("person.txt", ios::out | ios::binary) //创建流对象和打开文件合成一步
Person P = { "张三”, 18};
ofs.write(( const char *)&p, sizeof(Person));
//读文件
while(ifs.read((char*)&P, sizeof(Person)))
{
cout <<
}
cin.ignore() ——舍弃输入流缓冲区中的第一个字符,与cin.get()/getchar效果相同。
cin.ignore(int n)——舍弃输入流缓冲区中的前n个字符。
cin.ignore(int n,char delim)——舍弃输入流缓冲区中的前n个字符,或遇到结束符delim为止。
cin.sync——清空输入缓冲区。
cout.put(char c)——向输入流中插入一个字符。
cout.put('C').put('+').put('+').put('\n');
cout.put('C')<<"++"<<endl;
cout.write(const* s, int n)
经典用法——cout.write(str, strlen(str));
str——指向字符串的字符指针或字符数组的名字或字符串常量。
int width = 4;
char str[20];
cin.width(5); //cin>>str;实际只能提取4个字符,str最后一个是空字符,其他的放在流中等待接受。
while( cin >> str )
{
cout.width(width++); //将4个字符输出,设置每次输出的域宽增加1
cout << str << endl; //输出str
cin.width(5); //设置接受4个字符
}
cout.precision( 5 );//设置浮点数的精度——有效数字的位数
cout << fixed << setprecision(n); //n为小数位数
cout << a;
函数默认参数值——有默认参数的形参必须列在形参列表的最右,调用时实参与形参的结合次序是从左向右。
int ADD(int x = 5, int y = 6)
{
return x + y;
}
int main()
{
ADD(10, 20);//10+20
ADD(10); //10+6
ADD(); //5+6
}
注:若函数有原型声明(在函数定义之前),则默认参数在原型声明中给出,函数的定义处不能再指定默认值。
编写一程序建立一文件"student.dat",并从键盘输入3个学生的数据到该文件中。每个学生的数据包括姓名、学号以及数学、英语、计算机等课程的成绩。将文件"student.dat"中的数据读出来,计算每个同学的总分,并显示在屏幕上。
#include<iostream>
#include<fstream>
#include<iomanip>
using namespace std;
int main()
{
ofstream outfile("student.dat");
char name[8], id[8];
int math, eng, computer, sum;
for(int i = 0; i < 3; i++)
{
cout << "name:"; cin >> name;
cout << "stno:"; cin >> id;
cout << "math:"; cin >> math;
cout << "eng:"; cin >> eng;
cout << "computer:"; cin >> computer;
outfile << name << " " << id << " " << math << " " << eng << " " << computer << endl;
}
outfile.close();
ifstream infile("student.dat");
cout << setw(10) << "name" << setw(10) << "stno" << setw(10) << "math" << setw(10) << "eng" << setw(12) << "computer" << setw(10) << "sum" << endl << endl;
infile >> name;
while(!infile.eof())
{
infile >> id >> math >> eng >> computer;
sum = math + eng + computer;
cout << setw(10) << name << setw(10) << id << setw(10) << math << setw(10) << eng << setw(12) << computer << setw(10) << sum << endl;
infile >> name;
}
infile.close();
}
memset对数组进行初始,常用于清空内存。
二维整型数组直接利用memset() 函数初始化时,只能初始化为 0 或 -1 ,否则将会被设为随机值。
二维 char 型数组利用memset() 函数初始化时不受限制,可以初始化任意字符。
int a[10][10];
memset(a,0,sizeof(a));
char a[10][10];
memset(a,'#',sizeof(a));
用引用交换值
void swap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
int main()
{
swap(a, b);
}
int a;
const int b = 1;//常量必须初始化
int &c1 = b;//error
const int &c2 = a;//right
const int &c2 = b;//right
const int &c3 = a+b;//right
引用数组
int a[10] = {1, 2, 3};
int (&ra)[10] = a;
使用auto让编译器去推断引用于指针的类型
int a = 10;
auto *p = &a;//p的类型为int*
auto& r = a;//r的类型为int
const int b = 20;
auto& rr = b;//rr的类型为const int
右值引用
T&& rvref = expr;//expr必须是一个右值表达式
int a;
int &&e1 = a+1;
int &&e2 = 2;
把引用作为返回值:当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
double& setValues(int i) {
double& ref = vals[i];
return ref; // 返回第 i 个元素的引用,ref 是一个引用变量,ref 引用 vals[i]
}
int main ()
{
setValues(1) = 20.23; // 改变第 2 个元素
setValues(3) = 70.8; // 改变第 4 个元素
}
引用传递大型的类对象或数据结构
const_cast——常量强制转换, 主要用来删除数据的const限制(数据属性还是const,并没有真正去除),将常量对象转换成非常量对象。
using namespace std;
void ConstTest1(){
const int a = 1;
int *p;
p = const_cast<int*>(&a);
(*p)++;
cout<<a<<endl; //1
cout<<*p<<endl; //2
}
void ConstTest2(){
int i=3;
const int a = i;
int &r = const_cast<int &>(a);
r++;
cout<<a<<endl; //4
}
int main(){
ConstTest1();
ConstTest2();
return 0;
}
for(string s; getline(cin, s);)
for(int i=0;i<s.length();i++)//或s.size()
动态内存
int *p = new int[100]{ };
delete []p;
智能指针 unique_ptr
#include<memory>
int main()
{
int *p = new int(110);
unique_ptr<int[]>p0(p);
unique_ptr<int[]>p0(new int[110]);
unique_ptr<int[]>p0 = make_unique<int[]>(110);
}
nique_ptr<int[ ]>func()
{
unique_ptr<int[ ]>pp(new int[110]);
return pp;
}
int main()
{
unique_ptr<int[ ]>p1(new int[110]);
unique_ptr<int[ ]>p2;
//p2 = p1;//错误,因为p1之后将继续存在
p2 = unique_ptr<int[ ]>(new int(110));//用匿名对象赋值
p2 = func();//用函数赋值,p2将变为pp。
p2 = nullptr;//释放对象
}
void func(const int *a)
{
}//不对指针负责
func(p0.get());//get()方法返回裸指针。例如当一个函数需要一个原始指针时.
void func(int *a)
{
delete a;//对函数负责。
}
func(p0.relese());//释放对原始指针的控制权,p0成为空指针。
void func(unique_ptr<int[ ]> &p0)// 智能指针用于函数的参数时,传引用
{
}//不对指针负责
func(p0);
void func(unique_ptr<int[ ]> p0)
{
}
func(move(p0));//转移对原始指针的控制权_此时交给func函数的形参
p0.reset(); //释放p中内置的指针指向的空间
p0.reset(new int[110]); //释放p之前所指的空间,同时指向新的空间
p0.swap(p1);//交换两个指针的控制权
shared_ptr
shared_ptr<int[ ]>p1(new int[110]);
shared_ptr<int[ ]>p0(new int[110]);
cout << p0.use_count() << endl;//显示p0引用计数的值。
shared_ptr<int[ ]>p0(p1);//用已经存在的shared_ptr初始化,计数加一
shared_ptr<int[ ]>p0 = p1;//用已经存在的shared_ptr初始化,计数加一
shared_ptr支持赋值,左值shared_ptr的计数器将加一,右值shared_ptr的计数器将减一。
指向资源的指针多了一个,引用计数就加一,指向资源的指针少了一个,引用计数就减一,如果引用计数归零了,就释放资源
调用有参的构造函数
1.括号法
Person p1;//无参构造函数不能加括号。
Person p2(10);
Person p3(p2);
2.显式法
Person P2 = Person(10);//Person(10)单独写就是匿名对象,当前行结束之后,马上析构
Person p3 = Person(p2);//不能用拷贝构造函数初始化匿名对象,编译器认为是对象声明。Person(p4)
3.隐式转换法
Person p4 = 10;//Person p4 = Person(10);
Person p5 = p4;//Person p5 = Person(p4);
拷贝构造函数调用时机
1.使用一个已经创建完毕的对象来初始化一个新对象
Person p1(20);
Person p2(p1);
2.值传递的方式给函数参数传值
void dowork(Person p) //仅是值传递,和下方p并不相同,不会改变p
{
}
void test()
{
Person p;
dowork(p);
}
3.值方式返回局部对象
Person dowork()
{
Person p1;
return p1;//这里返回的p1和上面的p1并不相同
}
void test()
{
Person p = dowork();
}
C++编译器给至少一个类添加三个函数
1.默认构造函数
2.默认析构函数
3.默认拷贝构造函数(对属性进行值拷贝)
注:1.如果定义有参构造函数,将不再提供默认无参构造,但提供默认拷贝构造
2..如果定义拷贝构造函数,将不再提供其他构造函数。
深拷贝与浅拷贝
浅拷贝:堆区内存重复释放
class Person
{
public:
Person(){}
Person(int age, int height)
{
m_age = age;
m_height = new int(height);
}
~Person()//浅拷贝会将p1,p2指向的同一个地址释放两次
{
if(m_height != NULL)
{
delete m_height;
m_height = NULL;
}
}
int m_age;
int *m_height;
}
void test()
{
Person p1(18, 170);
cout << p1.m_age << p1.m_height;
Person p2(p1);
cout << p2.m_age << p2.m_height;
}
深拷贝
Person(const Person &p)
{
m_age = p.m_age;
m_height = p.m_height;//编译器默认实现的就是这行
//深拷贝
m_heighr = new int(*p.m_height);//堆区开辟一片内存空间
}
初始化列表
class Person
{
public:
Person(int a, int b, int c):m_A(a),m_B(b),m_C(c)
{
}
int m_A, m_B, m_C;
}
void test()
{
Person p(10,20,30);
}
类对象作为类成员
class Phone
{
public:
Phone(string pName)
{
m_PName = pName;
}
string m_PName;
};
class Person
{
public:
Person(string name, string Pname):m_Name(name), m_Phone(Pname)
{
}
string m_Name;
Phone m_Phone;
};
void test()
{
Person p("张三","苹果MAX");
cout << p.m_Name << "拿着" << p.m_Phone.m_PName << endl;
}
静态成员变量
1.静态成员变量不属于某个对象,所有对象共享同一份数据
2.类内申明,类外初始化
class Person
{
public:
static int m_A;
};
int Person::m_A = 100;
void test()
{
//通过对象进行访问
Person p;
cout << p.m_A << endl;//100
Person p1;
p1.m_A = 200;
cout << p1.m_A << endl;//200
cout << p.m_A << endl;//200
//通过类进行访问
cout << Person::m_A << endl;
}
静态成员函数
1.所有对象共享一个函数
2.只能访问静态成员变量(静态成员函数只属于类本身不属于每一个对象实例,非静态成员仅当实例化对象后才存在。静态成员函数产生在前,非静态成员函数产生在后,静态函数无法访问一个不存在的东西)
//通过对象进行访问
Person P;
p.func();
//通过类名访问
Person::func();
类的非静态成员函数才有this指针
class Person
{
Person(int age)
{
//this指针指向被调用的成员函数所属的对象
this->age = age;
}
Person& PersonAddAge(Person &p) //返回对象本体用Person&,如果不加&,则会创建一个新对象
{
this->age += p.age;
//this是指向p2的指针,*this指向的就是p2这个对象的本体。
return *this;
}
int age;
};
void test()
{
Person p1(10);
Person p2(10);
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);//多次调用时要返回p2,让p2继续加
}
空指针访问成员函数
class Person {
public:
void a()
{ };
void b()
{
if(this == NULL)//预防空指针
return;
cout << age << endl;
};
int age;
};
void test()
{
Person *p = NULL;
p->a();
p->b();//error 相当于cout << this->age <<endl;而p指向空指针this没有对象
}
const修饰成员函数
class Person {
public:
//Person * const this == void a();
//const Person * const this == void a() const;
void a() const//让指针指向值也不可被修改(常对象)
{
//this指针的本质是指针常量 指针的指向不可以被修改
m_a = 100;//error
m_b = 100;//right
};
void func()
{};
int m_a;
mutable int m_b;
};
void test()
{
Person p1;
const Person p2;//常对象
p2.m_a = 100;//error
p2.m_b = 100;//right
//常对象只能调用常函数,因为普通成员函数可以修改属性
p2.a();
p2.func();//error
}
1.全局函数做友元
class A
{
friend void getA(A *a);
public:
};
void getA(A *a)
{
}
2.类做友元
class a
{
friend class b;//b可以访问a的private
public:
}
3.成员函数做友元
//visit()要在类外面定义,a类要定义在b类前
class a
{
public:
void visit();
}
class b
{
friend void a::visit();
}
运算符重载
1.“+”
class A
{
public:
//成员函数重载+
A operator+(A &p)
{
A temp;
temp.m_a = m_a + p.m_a;
temp.m_b = m_b + p.m_b;
return temp;
}
int m_a;
int m_b;
};
//全局函数重载+
A operator+(A &p1, A &p2)
{
A temp;
temp.m_a = p1.m_a + p2.m_a;
temp.m_b = p1.m_b + p2.m_b;
return temp;
}
int main()
{
A a1;
A a2;
A a3 = a1 + a2;
}
2.“<<”
#include<iostream>
using namespace std;
class A
{
friend ostream &operator<<(ostream &cout, A &p);
public:
A(int a, int b)
{
m_a = a;
m_b = b;
}
private:
int m_a;
int m_b;
};
ostream &operator<<(ostream &cout, A &p) //重载后置递增返回的是一个值,此时去掉第二个&
{
cout << "m_a = " << p.m_a << " m_b = " << p.m_b;
return cout;
}
int main()
{
A a(10, 10);
//a.m_a = 10; //私有成员类外不可以访问
//a.m_b = 10; //但可以使用构造函数附值
cout << a << endl;
}
3.前/后置递增“++”
class A
{
public:
//重载前置++
A &operator++()//返回引用为了一直对一个数据进行递增操作
{
m_a++;
m_b++;
return *this;//返回自身
}
//重载后置++
A operator++(int) //int代表后置参数,用于区分前置和后置递增
{
A temp = *this; //记录当前本身的值
m_a++;
m_b++;
return temp;
}
int m_a;
int m_b;
};
int main()
{
A a;
++a;
}
4."="
class A
{
public:
A(int n)
{
m_a = new int(n);
}
~A()
{
if(m_a != NULL)
{
delete m_a;
m_a = NULL;
}
}
A& operator=(const A &p) //只能被重载为成员函数
{
if(m_a != NULL) //判断是否有属性在堆区
{
delete m_a;
m_a = NULL;
}
m_a = new int(*p.m_a); //深拷贝
return *this; //返回自身
}
int *m_a;
};
int main()
{
A a1(10);
A a2(20);
a1 = a2;
}
5."=="/"!="
class A
{
public:
A(string a, int b)
{
m_a = a;
m_b = b;
}
bool operator==(A &p)
{
if(this->m_a == p.m_a && this->m_b == p.m_b)
return true;
else return false;
}
string m_a;
int m_b;
};
int main()
{
A a1("abc", 10);
A a2("def", 20);
if(a1 == a2)
}
6.“()”
class A
{
public:
void operator()(string text)
{
cout << text << endl;
}
};
class B
{
public:
int operator()(int b1, int b2)
{
return b1 + b2;
}
};
int main()
{
A a;
a("hello world");
B b;
int n = b(10, 10);
cout << B()(10, 10) << endl; //匿名对象调用
}
父类所有非静态成员都会被子类继承下去
当子类与父类出行同名的成成员若要访问子类同名成员/成员函数,可直接访问,若要访问父类同名成员/成员函数,需要加作用域。
a.m_a;
a.Base::m_a;
同名静态成员/成员函数时
//通过对象访问
a.m_a;
a.Base::m_a;
//通过类名访问
Son::m_a;
Son::Base::m_a
多继承:
class Son : public Base1, public Base2
{
}
继承中如果父类出现了同名情况,子类使用的时候要加作用域。
虚继承——解决菱形继承问题
class Animal//虚基类
{
};
class Sheep : virtual public Animal{};
class Tuo : virtual public Animal{};
class SheepTuo : public Sheep, public Tuo{};
继承中如果父类出现了同名情况,子类使用的时候要加作用域。
多态满足条件:
1.有继承关系
2.子类重写父类的虚函数
多态使用:
父类指针或引用指向子类对象
Base *a = new Son1;
a->m_a = 10;
a->m_b = 10;
delete a;
a = new Son2;
a->m_a = 10;
a->m_b = 10;
delete a;
纯虚函数:virtual 返回值类型 函数名 (参数列表)= 0;
当类中有了纯虚函数,这个类也就变为抽象类。
抽象类特点:
1.无法实例化对象
2.子类必须重写抽象类中的纯虚函数,否则也属于抽象类.
虚析构与纯虚析构(析构父类)
1.解决父类指针释放子类对象
2.都需要有具体的函数实现
3.如果子类中没有堆区数据,可以不写虚析构或纯虚析构
虚析构:virtual ~类名(){}
纯虚析构:virtual ~类名() = 0;
类名::~类名(){}
函数模板
template<typename T>
显示指定类型:Swap<int >(a, b);
普通函数和显示指定类型可以发生隐式类型转换,自动类型推导不可以。
如果函数模板和普通函数都可以实现,优先调用函数模板。
可以通过空模板参数列表来强制调用函数模板:Swap<>(a, b);
函数模板可以发生重载。
template<class T>
bool Compare(T &a, T &b)
{
if(a==b)
}
//上面模板无法实现时,可以具体化,会优先调用
template<> bool Compare(A &p1, A &p2) //这里已经知道具体类型了
{
if(p1.m_a == p2.m_a && p1.m_b == p2.m_b) return true;
else return false;
}
类模板
template<class B, class C>
class A
{
public:
A(B b, C c)
{
this->m_b = b;
this->m_c = c;
}
B m_b;
C m_c;
};
int main()
{
A<string, int>("abcd", 100);
}
类模板在模板参数列表中可以有默认参数
template<class B, class C = int>
A<string>p("abcd", 100);
类模板中的成员函数在模板调用时才生成
class P1
{
void showp1(){};
}
class P2
{
void showp2(){};
}
template<calss T>
class A
{
T obj;
void func1(){obj.showp1();}
void func2(){obj.showp2();}
};
int main()
{
A<P1>a;
a.func1();
a.func2();//error
}
类模板对象作函数参数
//指定传入类型
A<string, int>a("abcd", 100);
void Print(A<string, int>&a)
{
}
//参数模板化
A<string, int>a("abcd", 100);
template<class T1, class T2>
void Print(A<T1, T2>&p)
{
}
//整个类模板化
A<string, int>a("abcd", 100);
template<class T>
void Print(T &p)
{
}
类模板与继承
当父类是一个类模板时,子类在声明的时候,要指定父类T的类型。
template<class T>
class Base
{
T a;
};
class Son:public Base<int>
{
};
如果想要灵活指定父类中T的类型,子类也需要变类模板
template<class T>
class Base
{
T a;
};
template<class T, class T1>
class Son:public Base<T>
{
T1 obj;
};
int mian()
{
Son<int, char>a;//父类为int类型,子类为char类型
}
类模板成员函数类外实现
template<class T1, class T2>
class A
{
public:
A(T1 a, T2 b);
void Print();
T1 m_a;
T2 m_b;
};
//类模板构造函数类外实现
template<class T1, class T2>
A<T1, T2>::A(T1 a, T2 b)
{
m_a = a;
m_b = b;
}
//类模板成员函数类外实现
template<class T1, class T2>
void A<T1, T2>::Print()
{}
类模板与友元
template<class T1, class T2>
class A
{
public:
//全局函数 类内实现
friend void Print(A<T1, T2>p)
{
}
A(T1 a, T2 b)
{
m_a = a;
m_b = b;
}
private:
T1 m_a;
T2 m_b;
};
int main()
{
A<string, int>p("abcd", 100);
Print(p);
}
template<class T1, class T2>
class A
{
public:
//全局函数 类外实现
friend void Print(A<T1, T2>p);
A(T1 a, T2 b)
{
m_a = a;
m_b = b;
}
private:
T1 m_a;
T2 m_b;
};
template<class T1, class T2>
void Print(A<T1, T2>p) //因为是全局函数,所以不需要加作用域
{
}
int main()
{
A<string, int>p("abcd", 100);
Print(p);
}
Person(){}
Person(string a, int b)
{
name = a;
id = b;
}
Person(string a="", int b=0)
{
name = a;
id = b;
}