c++学习笔记1
我是c++的新手,零基础学习,做了一些笔记,供大家参考和自己复习。
我是在B站上听的翁恺老师的课,翁恺老师的课讲的很好,感兴趣的可以去听。
因为我有c语言的基础,所以有时候我会拿c语言和c++做对比。
//
#include <iostream>
#include <stdlib.h>
using namespace std; //名字空间
int main()
{
cout << "hello world"<<endl;
system("pause");
return 0;
}
这是一段c++的模板,c语言开头是#include <io.h>,c++是#include 。如果要用到std,就要加上#include <stdlib.h>。std是一个类(输入输出标准),它包括了cin成员(标准输入)和cout(标准输出)成员,using name space std 以后才能使用它的成员。否则要写成std cout。输出格式如上所示,输入的格式与之类似,不过用的是">>"。system();是头文件<windows.h>库里面的一个函数。调用了系统的命令行,如:system(“clr”);实现清屏还可以实验文件删除,格式化磁盘等DOS,命令行的命令…
///
class Point3d{
public:
Point3d(float x,float y,float z);
print();
private:
float x;
float y;
float z;
};
c++里面必然要用到的是类,上面是类的格式,类用class定义,public是公共的;private是私有的,里面的变量被称为成员变量。
Point3d a(1,2,3);a是类的对象
a.print();这是在调用对象的函数
类的声明放在.h文件里面,类里面的函数的定义放在.cpp文件里面
c++的类,类似于c语言的struct,但是比struct的功能要更强大,当然c++也可以使用struct(struct和类的区别会在后面提到),所以说c++是c语言的扩展。
#ifndef TIC_H_
#define TIC_H_
class Tic{
public:
Tic();
virtual ~Tic();
void showPrompt();
void inserBalance(int money);
private:
//const int price;
int balance;
int total;
int i;
};
#endif
这是.h文件的格式
#include "Tic.h"
#include <stdio.h>
#include <iostream>
using namespace std;
void Tic::showPrompt()
{
cout << "something";
i=20;
printf("Tic::showPrompt()--&i=%p\n",&i);
}
void Tic::inserMoney(int money)
{
balance += money;
}
这是.cpp文件的格式
::f(),全局的。::是解析符。
需要注意的是:成员变量在类的对象里,不在这个类里;c++可以用c语言的库,但是printf不能和cout混在一个程序里。
class X{
int i;
public:
void f();
void grow(int b);
X(); //构造函数contructor (当对象被创造时调用)(没有返回类型)(可以用来初始化)
};
int main(){
cout<<"hellor"<<endl;
{
X a(10);//默认调用构造函数,10输入给构造函数
cout<<"hellor"<<endl;
a.f();
a.grow(4);
cout<<"hellor"<<endl;
}//出了大括号即a不存在
cout<<"hellor"<<endl;
}
上面的类X里面并没有定义成员变量的属性,是因为在c++中对于class而言,没有访问属性(private和public),默认为private,对于struct则默认为public。
上面介绍了{}的用法,在{}里面创造对象,出了大括号则a不存在。
使用类就必然要知道构造函数和析构函数,和类的名称同名的那个函数X()就是构造函数,它没有返回类型,创造对象的时候会自动调用构造函数,因此它可以被用做初始化 。
class Y{
public:
~Y(); //析构函数destructor (当对象被消灭时调用)(没有返回类型)
Y();
void f();
};
Y::~Y(){
cout<<"hellor"<<endl;
printsize();
};
int main(){
cout<<"hellor"<<endl;
{
Y a(10);//默认调用构造函数,10输入给构造函数
cout<<"hellor"<<endl;
a.f();
cout<<"hellor"<<endl;
}//出了大括号即a不存在(此时调用析构函数)
cout<<"hellor"<<endl;
}
析构函数是在构造函数的基础上加上一个小波浪,即~Y()。析构函数也没有返回类型,在对象被消灭的时候被自动调用。
///
struct X{float f; int i;};
X a[3]={{1.1},{1}};
struct Y{float f; int i; Y(int a);};//(struct和class是差不多的)Y(int a)是构造函数
Y b[]={Y(1),Y(2),Y(3)};
没有参数的构造函数叫default constructor。
一个对象没有做构造,是不能被析构的。
在c++里struct里面可以有函数。
//
new int;new int[10];
delete p;delete []p;先析构再收回空间
new,delete相当于c++版本的malloc和free
int * psome=new int [10];
delete [] psome;([]表示psome有很多个空间,不带的话只删第一个)
注意事项:
不用delete释放不是new分配的空间,不要用delete两次释放同一个空间,new带[],delete也要带[],delete删除到null指针是安全的。
class A{
private:
int i;
int *p;
public:
A(){p=0;i=0;cout<<"A::A()"<<endl;}
~A(){if(p) delete p;cout<<"A::~A(),i="<<endl;}//delete删除到null指针是安全的
void set(int i){this->i=i;}//this->i指的是类的对象的i,不是参数i(最后面会介绍this)
void f(){cout<<"hellow";}
};
//
struct X;//前向声明(说明有X)
struct Y{
void f(X*);
};
struct X{
private:
int i;
public:
void initialize();
friend void g(X*,int);
friend void Y::f(X*);
friend strucu Z;
friend void h();
};
friend使该类或者函数成为友元类或友元函数。在外部就可以访问到正常情况下无法访问到的私有属性和方法。有本书上大概是这样说的:C++的封装就像是一堵墙,可以保证墙里面物品的安全。友元就像是在墙上装了一扇门,这样就可以通过这扇门进入墙里面,使用里面的东西。这样做在某些情况下会让你很方便,但是它破坏了C++的封装性,所以要慎用。
如果结构体X在结构体Y的后面,而Y要用到X,那么可以做前向声明。
在c++中对于class而言,没有访问属性(private和public),默认为private,对于struct则默认为public。
/
class Point{
private:
const float x,y;
Point(float xa=0.0,float ya=0.0):y(ya),x(xa){});//y(ya),x(xa)是赋值,放在大括号外面会在构造函数之前初始化
}
放在大括号外面赋值是初始化,放在里面是赋值。
//
class Person{...};
class Currency{...};
class SavingsAccount{
public:
SavingsAccount(const char* name,const char* address,int cents);
~SavingsAccount();
void print();
private:
Person m_saver;
Currency m_balance;
};
SavingsAccount::SavingsAccount(const char* name,const char* address,int cents):m_saver(name,address),m_balance(0,cents){}//初始化放在大括号外面(所有的初始化必须放在这里,父类的也必须放在这里)
SavingsAccount::print(){
m_saver.print();
m_balance.print();
}
对象组成(组合是拿已有的对象拼装成一个新的对象),上面的就是对象组成。
一般情况下,我们都把初始化放在构造函数的大括号外面,之后我们会讲到父类子类的概念,父类的初始化也要放在子类构造函数的大括号外面。
//
继承(在已有的类上做改造得到一个新的类)
class A{
public:
A():i(0){cout<<"A::A()"<<endl;}
~A(){cout<<"A::~A()"<<endl;}
void print(){cout<<"A::print()"<<i<<endl;}
protected:
void set(int ii){i=ii;}//protected属性只有自己和子类能调用
private:
int i;
};
class B : public A{ //B是A的子类
public:
void f(){
set(20);
}
};
int main()
{
B b;
//b.set(20);这是错误的
b.print();
b.f();
return 0;
}
const std::string& name (把name定义为protected属性)。
子类格式如上所示,构造一个子类的对象的时候,他的父类的构造函数会被自动调用,父类先于子类构造,子类先于父类析构。父类的初始化应该放在子类的构造函数里,即父类构造函数需要的参数应该放在子类构造函数的大括号外面。protected属性只有自己和子类能调用,所以上面主函数里面的b.set(20);是错误的。
class A{
public:
A():i(0){cout<<"A::A()"<<endl;}
~A(){cout<<"A::~A()"<<endl;}
void print(){cout<<"A::print()"<<i<<endl;
void set(int ii){i=ii;}//protected属性只有自己和子类能调用
private:
int i;
};
class B : public A{ //B是A的子类
public:
B():A(15){cout<<"B::B()"<<endl;} //A先构造B再构造,B先析构
~B(){cout<<"B::~B()"<<endl;}
void f(){
set(20);
}
};
int main()
{
B b;
b.set(20);
b.print();
b.f();
return 0;
}
c++中若有几个相同的函数,编译器会根据你给出参数的数量和种类来判断该调用哪个函数。
cout是c++内置的输出流ostream。你可以自定义自己的输出流,ostream、ofstream都可以,很多程序喜欢用out这个名字,因为out代表输出的意思。
我们应该尽量的利用我们已经写好的代码,而不是让同样的代码在程序里到处都有,避免代码复制。
class Employee {
public:
Employee(const std::string& name,const std::string&ssn);
const std::string& get_name() const;
void print(std::ostream& out) const;
void print(std::ostream& out, const std::string& msg) const;
protected:
std::string m_name;
std::string m_ssn;
};
inline conet std::string& Employee::get_name() const {
return m_name;
}
inline void Employee::print(std::ostream& out) const {
out << m_name << endl;
out << m_ssn << endl;
}
inline void Employee::print(std::ostream& out, const std::string& msg) const {
out << msg <<endl;
print(out);
}
class Manager : public Employee {
public:
Manager(const std::string& name,const std::string&ssn,const std::string& title);
const std::string title_name() const;
const std::string& get_title() const;
void print(std::ostream& out) const;
private:
std::string m_title;
};
/
Employee::Employee(const string& name,const string& ssn) : m_name(name),m_ssn(ssn) {
//initializer list sets up the values!
}
对于c++来说,如果父类当中一个函数有几个不同的形式,在子类当中出现了和父类中重复的函数(相同的名字,参数表都一样),那么子类当中就只有子类自己的那一个函数了,父类当中的都被隐藏了。(其他语言中出现这种情况,这些函数是有关系的,即“你是我的替代品”,在c++中,他们是没有关系的)。
返回类型不能构成参数重载的条件。即函数名一样,参数一样,但返回值不一样。
函数缺省参数值
int harpo(int n,int m=4,int j=5);//(可以在这里带参数)(这是写在.h里面的,不能在.cpp里面再写一遍,也可以直接在主函数里面写出函数原型带参数)
int chico(int n,int m=6,int j);//这种形式是错误的
带参数必须从右边开始。
beeps=harpo(2);
beeps=harpo(1,8);
beeps=harpo(8,7,6);
我们可以在函数里面带上参数,即定义函数参数时给它赋个值,然后给值的时候可以不给已经赋了值的参数。带参数的写法要写在.h里面的,不能在.cpp里面再写一遍,否则会造成重复定义,也可以直接在主函数里面写出函数原型带参数。当然,这里只是提到有这种方法,一般不使用,这种方法并不安全。
//
内联函数(inline) :在函数前面加一个inline,就是一个inline函数,调用的时候是把函数的代码嵌入到调用他的地方去,但是又保持函数的独立性,这样做的目的是为了减少额外的工作。
因为编译器在执行一段程序的时候,会有入栈出栈等一系列的操作,增加了许多额外的工作,为了减少额外的工作,我们会在函数前面加上inline。
inline在.h和.cpp里面都必须重复说,函数的身体要放在.h文件里面,那么这个时候就会出现重复定义。函数前面加上inline以后,这个函数就不再是个定义而是个声明。因此inline不应该有.cpp文件,所有东西放在.h文件里面就行了。
inline其实就是牺牲代码的空间,来降低调用函数时候的额外的开销(以空间换时间)。
inline比c语言的宏定义要安全,宏定义不能做类型检查,inline作为函数可以由编译器来做类型检查。
#define f(a) (a)+(a)
main(){
double a=4;
printf("%d",f(a));
}
inline int f(int i){
double a=4;
printf("%d",f(a));
}
这里的代码,上一段是c语言,后面一段是c++,这里的c语言代码是由错误的,它是double,但输出的是int,但是这里骗过了编译器,不会报错,会把64位砍成32位输出,得到一个错误的结果,而c++的代码会提示你,是否需要强制转换。
注意:
1.递归需要进寨出寨操作,不能用inline。
2.当inline函数太复杂时,编译器可能会拒绝这个操作。
3.成员函数如果在声明时给出定义,那么这个函数就是inline,这个类只要有.h就够了。
class Point{
int i,j,k;//(没有定义属性则默认为是private)
public:
Point(){i=j=k=0};
.........
};
也可以把函数的定义放在类的后面,前面加上inline。让class看起来比较清爽。
如果函数比较小或者频繁的被调用,那么可以把他做成inline,非常大的(超过20行的)或者递归的函数不要去用inline。
/
const
const的变量初始化以后不能被赋值,不能被修改;这种const的变量对c++来说仍然是变量而不是常数,因为变量意味着要在内存里给你分配地址。
const int class_size=12;
int finalGrade[class_size];//OK
int x;
cin >> x;
const int size=x;
double classAverage[size];//error
这里之所以错误是因为编译器需要知道本地变量有多大,然后给他分配内存。
指针遇上const有两种情况
一种是q是const,一种是*q是const
char *const q="abc";//q是const
*q='c';//ok
q++;//error
const char *p="ABCD";//*p是const char
*p='b';//error(意思是你不能通过这个p去修改我所指定的内存单元,而不是我所指到哪,那个内存单元就变成const了)
Person p1("Fred",200);
const Person* p=&p1; //对象是const
Person const* p=&p1; //对象是const
Person *const p=&p1; //指针是const
看const是在*号前还是后(也可以在前面再加个const,那么指针和对象就都是const了)
*p是const的意思是,你不能通过这个p去修改我所指定的内存单元,而不是我所指到哪,那个内存单元就变成const了。
int i;
const int ci=3;
int *ip;
const int *cip;
ip=&i;
cip=&i;
ip=&ci;//error(可以通过ip修改他所指的内容,但是ci是const,不允许被修改)
cip=&ci;
int main()
{
char *s=“hellow world”;//会出现woring,应该在最前面加上const。 如果这句是char s[]=“hellow world”,那么这段程序是正确的,因为他会把代码段里的"hellow world"拷贝到堆栈里面
cout << s << endl;
s[0]=‘B’;
cout << s << endl;
return 0;
}
这种做法是错误的,本地变量放在堆栈里,全局变量在全局数据区里,而全局变量里的这种常量hellow world在代码段里,代码段是不可写的,这是一个const,不能被修改。
void f(const int* x);//可以把int或const int送给这个函数,他会保证在函数内不修改这个参数。
int a=15;
f(&a);//ok
const int b=a;
f(&b);//ok
b=a+1;//error
如果函数的参数是const int x,对于调用f的人无所谓,因为不是指针,只表明在f里面不改x。
int f2(){return 1;}
const int f3(){return 1;}
int main(){
const int j=f2();
int k=f3();//因为这只是赋值,并不修改f3()的返回结果。
}
这些操作都是可以的
const Currency the_raise(42,38)//整个对象都是const,这个对象里面的值是不能被修改的。我们要知道里面的函数会不会修改里面的成员变量,会修改变量的函数是不能执行的,我们需要知道这个对象里面哪些函数能执行,哪些不能执行
int Date::get_day() const {
…
}
我们可以在函数的后面加上const,意思是保证这个函数不修改任何成员变量(原型和定义的地方都需要加上const)。
int get_day() const;//实际上是说this是const。
关于this:
每个对象都拥有一个this指针,this指针记录对象的内存地址,当我们调用成员函数时,成员函数默认第一个参数为T* const register //this,大多数编译器通过ecx寄存器传递this指针,通过 this 这个隐式参数可以访问该对象的数据成员。
类的成员函数为什么不能用static和const同时修饰?
类中用const修饰的函数通常用来防止修改对象的数据成员,函数末尾的const是用来修饰this指针,防止在函数内对数据成员进行修改,而静态//函数中是没有this指针的,无法访问到对象的数据成员,与C++ static语义冲突,所以不能。
this指针注意点
1、C++中this关键字是一个指向对象自己的一个常量指针,不能给this赋值;
2、只有成员函数才有this指针,友元函数不是类的成员函数,没有this指针;
3、同样静态函数也是没有this指针的,静态函数如同静态变量一样,不属于具体的哪一个对象;
4、this指针作用域在类成员函数内部,在类外也无法获取;
5、this指针并不是对象的一部分,this指针所占的内存大小是不会反应在sizeof操作符上的。
this指针的使用
1、在类的非静态成员函数中返回类对象本身的时候,直接使用 return //this,比如类的默认取址运算符重载函数,另外,也可以返回this的引用,这样可以像输入输出流那样进行“级联”操作;
2、修改类成员变量或参数与成员变量名相同时,如this->a = a (写成a = a编译不过);
3、在class定义时要用到类型变量自身时,因为这时候还不知道变量名,就用this这样的指针来使用变量自身。
this是在对象new的过程中创建的
这是我的第一阶段的学习笔记,c++的内容还有很多,等着我去慢慢学习和积累。在我接触到c++后,发现我以前看待编程的眼光太窄了,这确实提升了我的视野,也感受到了编程的多样性。其实一个好的程序很考验程序员的排版能力和细心程度。多学多练也有利于提高你的熟练度和对一些潜在规则的应用,以及看代码写代码的速度,我觉得拥有良好的写代码的习惯和自己的写代码的风格是非常重要的,一直抄别人的代码尽管很快,各种意义上的快,但是没有积累的过程,你下一次 依旧想不起来这个代码该怎么打。
我们程序员一定要注意warning,不要因为程序能运行就忽视了它,一段代码、一个工程,我们可能都会多次使用,不要因为这个坑了自己。翁老师上课的时候讲了一个笑话,悬崖前面立一块牌子,上面写着warning,程序员都会掉下去。
细节决定成败,the more careful, the more relaxed。