学习笔记,小白可以相互学习,大佬看到能告诉咱理解不对的地方就好了。
代码十分拙劣。
入门书:C++ Primer Plus,C++ Primer
C++程序语言设计(不适合初学)
c++中包含c的头文件可采用#include<cstdio>//去掉.h,前面加c
编译由gcc变为g++,//g++ -o test1 test.cpp //g++ -std=c++0x -o test1 test1.cpp //g++ -std=c++11 -o test1 test1.cpp//-std=c++11是设定新标准
c与c++的编程步骤是一样的:编辑源码,编译过程:预处理,编译,汇编,链接
/*
* 运行GNU编译器的命令是g++;
* $ g++ -o prog1 prog1.cpp
*
* 运行微软的Visual Studio 编译器的命令是c1
* c1 /EHcs prog1.cpp (/EHsc是编译器选项,用来打开标准异常处理)
*
* c++的输入输出是用流(stream)的方式实现的。
* 标准输出cout()标准输入cin()标准出错cerr(),clog()
* 输入运算符>>,输出运算算符<<,作用域运算符::
*/
#include<iostream>
int main()
{
int sum = 0;
int i = 1;
while(i <= 10)
{
sum = sum + i;
i++;
}
std::cout<<"sum= "<<sum<<std::endl;
int c = 0;;
int val = 0;
if(std::cin>>c)
{
int b = 1;
while(std::cin>>val)
{
if(val == c)
{
++b;
}
else
{
std::cout<<c<<"occur"<<b<<"time"<<std::endl;
c = val;
b = 1;
}
}
}
return 0;
}
c++的兼容及差异:
内联函数:
函数重载:
c++的默认参数:
函数的默认参数: 1.c++允许赋予函数参数默认值,即在调用该函数时,可以不写某些参数的值,编译器会自动把默认值传递给调用语句中
2.默认值只能在声明中设置
注意: 1.不能将实际值传递给引用类型的参数,可以将变量作为引用类型参数的默认值,这时变量必须是已经声明且是全局变量
2.若给某一参数设置了默认值,那么在参数表中其后面的所有参数都必须也设默认值
3.在调用时,若给已经设置默认值的参数传递实际值,既要取代默认值,则在参数表中被取代的参数左边的所有参数,无论是否
有默认值,都必须设置传递的实际参数
#include <iostream>
int const MaxBufSize = 512;
//using namespace std;//不建议把所有命名空间的内容全部暴露在全局域中
//可以使用下面的,要用什么再声明什么
using std::cout;
using std::cin;
using std::endl;//读写数据时,数据是先被放到了内存中,系统再将其写入到文件中
//endl操作符是将调用的数据用完后自动清空缓冲区,同时换行
//c++中所有的运算符都可以看成是一种函数
namespace Teamone
{
int x;
}
namespace Teamtwo
{
int x;
}
int x;//全局变量的x,使用它可用::x = 7;
void myswap(int &a,int &b)
{
int t;
t = a;
a = b;
b = t;
}
int &inc(int &a)
{
a = a + 1;
return a;
}
inline int add(int e,int d)
//内联函数,(代码尽量少(一般5个语句以下),在调用时,编译器将直接展开他)
//内联函数的限制:可以节省运行时间,但是却增加了目标程序的长度
//由于多个源文件是分开编译的,要内联就应该把内联函数声明定义在头文件中
//内联函数中不能包括复杂的控制语句,例如循环语句和switch语句
{
return e + d;
}
double getarea(double r,double PI = 3.14)//函数有默认值,给其赋值的时候是从左到右
{
return r*r*PI;
}
int sum(int a)//函数的重载,c++允许函数重载,一个函数名可以多用,编译器会自动匹配合适的函数
{
return a+a;
}
int sum(int a,int b)
{
return a+b;
}
int sum(double a,int b)
{
return a+b;
}
int sum(int a,int b,int d)
{
return a+b+d;
}
int sum(int a,int b,int d,int e)
{
return a+b+e+d;
}
int main()
{
char buf[MaxBufSize];
char c = 'a';
int i =(int)c;
int j = int(c);//c++中更加偏向于这种
cout << "Hello c++" << endl;
cin>>buf;
cout<<"buf="<<buf<<" i="<<i<<" j="<<j<<endl;
int a = 88;
int b = 99;
int &ra = a;
cout<<"a="<<a<<" ra="<<ra<<endl;
myswap(a,b);
cout<<"a="<<a<<" b="<<b<<endl;
inc(a);
cout<<"inc(a)="<<a<<endl;
inc(a) = 1;
cout<<"&inc(a)="<<a<<endl;
int e = 5;
int d = 6;
int k = add(e,d);
cout<<"e+d="<<k<<endl;
double r = 5.6;
double n = getarea(r);
cout<<"area="<<n<<endl;
n = getarea(r,3);
cout<<"area="<<n<<endl;
n = getarea(r,3.1415);
cout<<"area="<<n<<endl;
//允许函数重载,限制:仅仅只能参数类型或者个数类型不同
k = sum(a);
cout<<"a+a="<<k<<endl;
k = sum(a,b);
cout<<"a+b="<<k<<endl;
k = sum(a,b,d);
cout<<"a+b+d="<<k<<endl;
k = sum(a,b,d,e);
cout<<"a+b+d+e="<<k<<endl;
k = sum(r,b);
cout<<"r+b="<<k<<endl;
char *p;
p = new char;//使用new关键字进行动态的内存分配
delete p;//使用delete释放掉内存
char *q;
q = new char [1024];//q有1024个char类型的内存大小
delete []q;//释放掉q的内存
Teamone::x = 5;
cout<<"Teamone x="<<Teamone::x<<endl;
cout<<"Teamtwo x="<<Teamtwo::x<<endl;//未初始化一般为0
Teamtwo::x = 6;
cout<<"Teamtwo x="<<Teamtwo::x<<endl;
::x = 7;//使用的是全局变量的x,用::区分
cout<<"::x="<<::x<<endl;
int ii,jj;
cin>>ii>>jj;
//cout<<"ii="<<ii<<" jj="<<jj<<endl;
cout<<"ii ="<<std::showbase<<std::hex<<ii<<" jj="<<jj<<endl;//ii,jj都是按照那个格式输出
//showbase输出整数时显示基数符号(0,8进制,0x,16进制)
//hex设置整数的基数是16,即是按照16进制输出
system("pause");//按任意键继续
return 0;
}
动态内存:
c++中引入了new/delete来分配释放内存
char *p;
p = new char;//使用new关键字进行动态的内存分配
delete p;//使用delete释放掉内存
c++标准的名字空间属性:
声明区:可以进行声明的区域
潜在的作用域:变量潜在的作用域,从声明点到声明区结尾
变量并非在潜在的作用域都可见,只要存在嵌套声明区,,且和内部声明区的变量重名,则会被内部声明区的变量隐藏
每个声明区都可以声明名称,且相互独立。也就意味着,即是不同的声明区出现了同名,也不会发生冲突
c++输入输出:
c++的输入输出是用“流”stream的方式实现的
输入:cin>>变量1>>变量2.....; 系统会自动识别输入数据的类型
输出:cout<<"xxx"<<"xx"......;
cin和cout语句都分成若干行
格式控制符
cout<<"ii ="<<std::showbase<<std::hex<<ii<<" jj="<<jj<<endl;//ii,jj都是按照那个格式输出
//showbase输出整数时显示基数符号(0,8进制,0x,16进制)
//hex设置整数的基数是16,即是按照16进制输出
设置状态标志流成员函数setf
一般格式long ios::setf(long flags)
调用格式:流对象.setf(ios::状态标志)
状态标志 | 功能 | i/o |
skipws | 跳过输入中的看空白字符 | i |
left | 输出数据在本域宽范围内左对齐 | i |
right | 输出数据在本域宽范围内右对齐 | i |
internal | 数据的符号位左对齐,,数据本身右对齐,符号和数据之间为填充符 | i |
dec | 设置整数的基数是10(10进制) | i/o |
oct | 设置整数的基数是8(8进制) | i/o |
hex | 设置整数的基数是16(16进制) | i/o |
showbase | 输出整数时显示符号基数(0,8进制,0x,16进制) | i |
showpoint | 浮点输出时带有小数点 | i |
uppercase | 以科学表示法格式E和十六进制输出字母时用大写表示 | i |
shoupos | 正整数前显示+号 | i |
scientific | 用科学表示法显示浮点数 | i |
fixed | 用定点格式(固定小数位数)显示浮点数 | i |
unitbuf | 完成输出操作后立即刷新所有流 | i |
stdio | 完成输出操作后刷新stdout和stderr | i |
类和对象
c++是一种面向对象程序设计方法发明的一种编程语言
类的声明定义:
1.c++中对象的类型称为类(class),类代表某一批对象的共性和特征,类是对象的抽象,而对象是类的具体实例
2.先声明一个类类型,然后用他去定义若干同类型的对象,对象就是类类型的一个变量,可以说类是对象的模板,是用来定义对象的一种抽象类型
3.类是抽象的,不占用内存,而对象是具体的,占用存储空间
4.类是用户自己指定的类型。如果程序中要用到类类型,必须根据需要进行声明,或者使用别人已设计好的类。
c++标准本身不提供现成的类的名称结构和内容。
编写一个类的基本语法:
class 类名
{
pulbic:
公用的数据和成员函数;
private:
私有的数据和成员函数;;//只能在class内部使用
protected:
保护的数据和成员函数;
}
下面是用类的思想实现的TCP服务器
/********tcpserver.h************************************/
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <string>
class TcpServer
{
public:
TcpServer(std::string ip, unsigned short port);
~TcpServer();
bool waitForConnect();
void disconnectFromHost();
int read(char *buf, unsigned int len);
int write(const char *buf, unsigned int len);
private:
int listenfd;
int connfd;
};
#endif
/**********tcpserver.cpp**************************8*******/
#include "tcpserver.h"
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
TcpServer::TcpServer(std::string ip, unsigned short port)
{
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
exit(EXIT_FAILURE);
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
inet_pton(AF_INET, ip.c_str(), &addr.sin_addr);
if (bind(listenfd, (const sockaddr *)&addr, sizeof(addr)) == -1)
exit(EXIT_FAILURE);
if (listen(listenfd, 1024) == -1)
exit(EXIT_FAILURE);
}
TcpServer::~TcpServer()
{
close(connfd);
close(listenfd);
}
bool TcpServer::waitForConnect()
{
if ((connfd = accept(listenfd, NULL, NULL)) == -1)
return false;
else
return true;
}
void TcpServer::disconnectFromHost()
{
close(connfd);
}
int TcpServer::read(char *buf, unsigned int len)
{
return ::read(connfd, buf, len);
}
int TcpServer::write(const char *buf, unsigned int len)
{
return ::write(connfd, buf, len);
}
/*****************main.cpp********************************************/
#include "tcpserver.h"
#include <cstring>
int main(int argc, char *argv[])
{
TcpServer server("0.0.0.0", 8888);
while (true) {
if (!server.waitForConnect())
continue;
while (true) {
int ret;
char buf[64];
ret = server.read(buf, sizeof(buf));
if (ret <= 0) {
server.disconnectFromHost();
break;
}
buf[ret] = '\0';
server.write(buf, strlen(buf));
}
}
}
/******clientclass.cpp****************************/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<pthread.h>
#include<sqlite3.h>
#include<time.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
using namespace std;
class tcpserver
{
public:
tcpserver(char *ip,short int port);
~tcpserver();
bool connection();
void disconnect();
int swrite();
int sread();
private:
int listenfd;
int connfd;
};
int main()
{
char *ip;
char buf[] = "0.0.0.0";
ip = buf;
short int port;
port = 8888;
tcpserver server(ip,port);
while(1)
{
server.connection();
while(1)
{
server.swrite();
server.sread();
}
}
return 0;
}
tcpserver::tcpserver(char *ip,short int port)
{
int ret;
struct sockaddr_in srvaddr;
//1.socket
listenfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == listenfd)
{
perror("socket");
exit(-1);
}
//2.bind
memset(&srvaddr,0,sizeof(struct sockaddr_in));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(port);
srvaddr.sin_addr.s_addr = inet_addr(ip);
ret = bind(listenfd,(const struct sockaddr *)&srvaddr,sizeof(srvaddr));
printf("%d\n",port);
if(-1 == ret)
{
perror("bind");
exit(-1);
}
printf("bind success\n");
//3.listen
ret = listen(listenfd,1024);
if(-1 == ret)
{
perror("listrn");
exit(-1);
}
printf("linsten success");
}
tcpserver::~tcpserver()
{
close(listenfd);
close(connfd);
}
bool tcpserver::connection()
{
socklen_t addrlen;
struct sockaddr_in ctladdr;
memset(&ctladdr,0,sizeof(ctladdr));
addrlen = sizeof(ctladdr);
connfd = accept(listenfd,(struct sockaddr *)&ctladdr,&addrlen);
if(-1 == connfd)
{
perror("accept");
return false;
}
printf("connect success\n");
return true;
}
int tcpserver::swrite()
{
char buf[64] = "world!";
int ret;
ret = write(connfd,buf,sizeof(buf));
if(-1 == ret)
{
perror("write");
return -1;
}
sleep(3);
printf("%s\n",buf);
}
int tcpserver::sread()
{
char buf[64];
int ret;
ret = read(connfd,buf,sizeof(buf));
if(-1 == ret)
{
perror("read");
return -1;
}
sleep(3);
printf("%s\n",buf);
}
/*********serverclass.cpp********************************************/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<pthread.h>
#include<sqlite3.h>
#include<time.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
using namespace std;
class tcpclient
{
public:
tcpclient(int sockfd);
~tcpclient();
bool connection(char *ip,short int port);
void disconnect();
int cwrite();
int cread();
private:
int sockfd;
};
int main()
{
int sockfd1;
sockfd1 = socket(AF_INET,SOCK_STREAM,0);
tcpclient client(sockfd1);
char *ip;
char buf[] = "0.0.0.0";
ip = buf;
short int port = 8888;
while(1)
{
client.connection(ip,port);
while(1)
{
client.cread();
client.cwrite();
}
}
return 0;
}
tcpclient::tcpclient(int sockfd1)
{
int ret;
//1.socket
sockfd = sockfd1;;
//socket(AF_INET,SOCK_STREAM,0);
if(-1 == sockfd)
{
perror("socket");
exit(-1);
}
}
//2.connect
bool tcpclient::connection(char *ip,short int port)
{
int ret;
struct sockaddr_in srvaddr;
memset(&srvaddr,0,sizeof(struct sockaddr_in));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(port);
srvaddr.sin_addr.s_addr = inet_addr(ip);
ret = connect(sockfd,(const struct sockaddr *)&srvaddr,sizeof(srvaddr));
printf("%d\n",port);
if(-1 == ret)
{
perror("connect");
return false;
}
printf("connect success\n");
return true;
}
tcpclient::~tcpclient()
{
close(sockfd);
}
int tcpclient::cwrite()
{
char buf[64] = " hello";
int ret;
ret = write(sockfd,buf,sizeof(buf));
if(-1 == ret)
{
perror("write");
return -1;
}
sleep(3);
printf("%s\n",buf);
}
int tcpclient::cread()
{
char buf[64];
int ret;
ret = read(sockfd,buf,sizeof(buf));
if(-1 == ret)
{
perror("read");
return -1;
}
sleep(3);
printf("%s\n",buf);
}
/**********Makefile******************************************/
all:
g++ serverclass.cpp -o serverclass
g++ clientclass.cpp -o clientclass
.PHONY:clean
clean:
rm serverclass clienclass
如果设计一个类时,没有显示声明定义构造函数,析构函数,复制构造函数,赋值运算符,地址运算符,则编译器会自动生成。
构造和析构
构造函数://构造函数,对cat进行初始化,
//1.构造函数的名字必须与类名一样
//2.没有返回值
//3.函数功能由用户定义,参数可有可无
//4.如果用户不设计则编译器自动生成一个,默认的构造函数
//5.不需要用户调用它也不能调用,而是在建立对象时自动执行
构造函数可以重载;
拷贝构造函数:
有时候需要用一个类对象初始化该类的另一个对象,设计一个拷贝构造函数,来实现一个类对象该类的另一个对象作拷贝是通过依次拷贝每个非静态数据成员
浅拷贝是:系统默认生成的,共用一个内存空间。深拷贝需要用户自定义,开辟空间。
析构函数:
//1.析构函数是一个对象的生命周期即将结束的时候应该回收对象占有的资源,或者是完成一些清理工作
//2.析构函数没有参数和返回值,因此它不能被重载
//3.析构函数的语法是在类名前加~
//4.当对象消亡时析构函数会自动被调用
//5.可以显示调用析构函数。一般不需要进行析构函数的显示,也会有特殊需求,
//例如:对象时静态的时候,存放在堆区分配,当程序未结束时,需要释放堆,
//这个时候就可以显示调用析构函数来完成。如果显示调用了析构函数,此时析构函数
//和普通成员函数是一样的,并不会造成对象被销毁。
//析构函数不写,编译器也会自动生成
//当需要在对象释放的时候销毁对象,必须采用析构函数
this指针:
this指针是一个特殊的指针,指向类对象自身的首地址
每个类对象的成员函数都是一个this指针,指向调用对象,如果要引用整个对象则*this
this指针仅能在类内部使用
static成员:
静态成员没有对象的this指针
静态成员不和具体的对象关联,也不能直接访问类的其他成员
const成员函数:
如果需要保证成员函数不会修改对象,譬如成员函数内部要使用this指针,则需要const成员函数
在类内声明语法形式:<数据类型><函数名>(<参数列表>)const;
在类外定义语法形式:<数据类型><类名>::<函数名>(<参数列表>)const;
如果一个对象被声明为常对象,则不能调用该对象的非const型的成员函数。const函数只能访问const成员
const数据成员的值是不能改变的,故此,只能通过构造函数的参数初始化列表对const数据成员进行初始化
例如:Cat::cat(int val) :ival(val) //后面的:ival(val)就是初始化列表
using namespace std;
class Cat//定义一个类
{
public:
char *type;
Cat(const char *t = "")
//构造函数,对cat进行初始化,
//1.构造函数的名字必须与类名一样
//2.没有返回值
//3.函数功能由用户定义,参数可有可无
//4.如果用户不设计则编译器自动生成一个,默认的构造函数
//5.不需要用户调用它也不能调用,而是在建立对象时自动执行
{
type = new char[32];
strncpy(type,t,32);
}
//当编译器自动生成的合成默认构造函数,不能达到自己所需要的要求的时候,就必须自己定义一个构造函数
/*Cat()
{
type = new char[32];
strncpy(type,"",32);
}*/
Cat::Cat(const Cat &c)
{
type = new char[32];
strncpy(type,c.type,32);
}
//当编译器自动生成的浅拷贝构造函数,不能满足需求时,就需要自己定义一个深拷贝构造函数
//拷贝构造函数,一般他和析构函数是一起出现的
~Cat()
//1.析构函数是一个对象的生命周期即将结束的时候应该回收对象占有的资源,或者是完成一些清理工作
//2.析构函数没有参数和返回值,因此它不能被重载
//3.析构函数的语法是在类名前加~
//4.当对象消亡时析构函数会自动被调用
//5.可以显示调用析构函数。一般不需要进行析构函数的显示,也会有特殊需求,
//例如:对象时静态的时候,存放在堆区分配,当程序未结束时,需要释放堆,
//这个时候就可以显示调用析构函数来完成。如果显示调用了析构函数,此时析构函数
//和普通成员函数是一样的,并不会造成对象被销毁。
//析构函数不写,编译器也会自动生成
//当需要在对象释放的时候销毁对象,必须采用析构函数
{
cout<<type<<"~cat()"<<endl;
delete[] type;
}
void eat()
{
cout<<type<<" :eat fish"<<endl;
}
void makenoise()
{
cout<<type<<" :miao..."<<endl;
}
};
int main()
{
int i = 8;
Cat cat2((char *)"tianyuan"),cat4(cat2);
if(i > 3)
{
Cat cat1((char *)"bosi");
cat1.eat();
cat1.makenoise();
}
cat2.eat();
cat2.makenoise();
cat2.~Cat();
Cat cat3;
cat3.eat();
cat3.makenoise();
cat4.eat();
cat4.makenoise();
system("pause");
return 0;
}
/***********cat.cpp*************************************/
#ifndef CAT_H
#define CAT_H
class Cat//定义一个类
{
public:
Cat(const char *t = "",int s = 0);//构造函数必须对其进行初始化
~Cat();//析构函数
Cat(const Cat &c);//拷贝构造函数
void eat()const;
void makenoise() const;
static int getCnt();//函数前面加static表明函数只能访问static的成员
int getsex() const;
private:
char *type;
static int cnt;//加了static 使cnt独立于任何对象之外,被所有对象共用
const int sex;
};
#endif // CAT_H
/***********cat.h**********************************/
#include"cat.h"
#include<cstring>
#include<iostream>
using namespace std;
int Cat::cnt = 0;
Cat::Cat(const char *t,int s) :sex(s)//:sex(s)初始化列表
{
type = new char[32];
strncpy(type,t,32);
cnt++;
}
Cat::Cat(const Cat &c) :sex(c.sex)
{
type = new char[32];
strncpy(type,c.type,32);
cnt++;
}
//当编译器自动生成的浅拷贝构造函数,不能满足需求时,就需要自己定义一个深拷贝构造函数
//拷贝构造函数,一般他和析构函数是一起出现的
Cat::~Cat()
{
delete[] type;
cnt--;
}
void Cat::eat() const//void Cat::eat(Cat *this)
{
cout<<type<<" :eat fish"<<endl;//cout<<this->type<<" :eat fish"<<endl;
}
//this指针是一个特殊的指针,指向类对象自身的首地址,this指着只能在类内部使用
//每个类对象的成员都是一个this指针,指向调用对象,如果要引用整个对象则*this
void Cat::makenoise() const
{
cout<<type<<" :miao..."<<endl;
}
int Cat::getCnt()
{
return cnt;
}
int Cat::getsex() const
{
return sex;
}
/************main.cpp****************************************************/
#include <iostream>
#include"cat.h"
using namespace std;
int main()
{
/*int i = 8;
Cat cat2((char *)"tianyuan"),cat3(cat2);
cout<<"cnt="<<Cat::getCnt()<<endl;
if(i > 3)
{
Cat cat1((char *)"bosi");
cat1.eat();
cat1.makenoise();
}
cat2.eat();//cat2.eat(&eat);
cat2.makenoise();
cat2.~Cat();
cat3.eat();
cat3.makenoise();
*/
int i, j = 5, k = j;
Cat cat1, cat2("bosi", 1), cat3(cat2);
cat1.eat();
cat1.makenoise();
cout << cat1.getCnt() << endl;
cat2.eat();
cat2.makenoise();
//cat2.~Cat();
cout << Cat::getCnt() << endl;
cat3.eat();
cat3.makenoise();
cout << cat1.getCnt() << endl;
Cat cat4;
cout << Cat::getCnt() << endl;
if (j > 3) {
Cat cat5;
cout << cat5.getCnt() << endl;
}
cout << cat4.getCnt() << endl;
cout << cat2.getsex() << endl;
cout << cat4.getsex() << endl;
return 0;
}
友元
类实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,仅仅通过类的成员函数才能读写。
运算符一般的不会是类的成员函数,二元运算符重载就需要友元
友元破坏了类的隐藏与封装,需要慎用
友元关系不能被继承。
友元函数:
#include <iostream>
#include<cmath>
using namespace std;
class Point
{
public:
friend double getDistance(Point &a,Point &b);
//友元函数(friend+函数),他可以访问pricate中的成员
//但是破坏了类的隐藏与封装,需慎用
Point(int x = 0,int y = 0);
int getx();
int gety();
void setx();
void sety();
private:
int x;
int y;;
};
Point::Point(int x,int y)
{
this->x = x;
this->y = y;
}
int Point::getx()
{
return x;
}
int Point::gety()
{
return y;
}
void Point::setx()
{
this->x = x;
}
void Point::sety()
{
this->y = y;
}
double getDistance(Point &a,Point &b)
{
return sqrt((a.x - b.x)*(a.x - b.x)+(a.y - b.y)*(a.y - b.y));
}
int main()
{
Point p1(0.0,0.0),p2(3.0,4.0);
cout<<"p1-->p2 distance is: "<<getDistance(p1,p2)<<endl;
return 0;
}
友元成员函数:类A的成员函数可以使类B的友元
运算符重载
函数可以重载,运算符也可以重载。
c++中预定义的运算符的操作对象只能是基本的数据类型,但是实际上,对于许多用户自己定义的类型(例如类),
也需要类似的操作。c++中的运算符十分灵活,用户可以自己重载运算符,赋予已有的运算符新的功能。
运算符重载一般语法如下:
<返回类型说明符>opreator<运算符符号>(<参数表>)
{
<函数体>;
}
可以被重载的运算符有:
算数运算符:+,-,*,/,%。,++,--
位操作运算符:&,|,~,^,<<,>>
逻辑运算符:&&,||,!
比较运算符:<,>,=,<=,>=,==,!=
赋值运算符:=,+=,-=,*=,/=,%=,&=,^=,|=,<<=,>>=
其他运算符:[],(),->,,new,","(逗号),delete,new[],delete[],*指针运算符
不能被重载的运算符有:成员访问运算符".",三目运算符"? :",sizeof,作用域"::"
友元运算符重载的语法形式:
class 类名
{
friend 返回类型 opreator运算符(形参表);
}
类外定义格式:
返回类型 opreator运算符(参数表)
{
函数体;
}
一般,友元函数重载双目运算符,参数表中参数应该为2个,重载单目运算符参数表中应该有一个参数
成员函数运算符重载:
语法形式
class 类名
{
返回类型 opreator 运算符(形参表);
}
类外定义格式:
返回类型 类名::opreator运算符(形参表)
{
函数体;
}
一般,对于成员函数重载运算符,双目运算符只有一个参数,而单目运算符没有参数。比起友元运算符重载少了一个
参数的原因在于,友元函数没有this指针。
注意事项:
1.除成员运算符".",作用域运算符"::",sizeof运算符和三目运算符"? : "以外,
c++中所有的运算符都可以重载(“&”几乎不用用户重载)
2.重载运算符限制在c++语言中已有的运算符范围内的允许重载的运算符,不能创建新的运算符
3.运算符重载的实质是函数重载,遵循函数重载的选择原则
4.重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构
5.运算符重载不能改变运算符用于该内部类型对象的含义
6.运算符重载是针对新类型数据的实际需要对原有的运算符进行适当的改造,重载的功能应该与原来的功能相类似,避免没有目的地使用重载运算符
7.重载运算符函数不能有默认的参数,否则就改变了运算符的参数个数
8.重载的运算符只能是用户自定义类型,否则就不是重载而是改变了现有的c++标准数据类型的运算符规则
9.运算符重载可通过成员函数的形式,也可以是通过友元函数,非成员非友元的普通函数
#include <iostream>
#include<string>
int main()
{
std::string str1,str2;
str1 = "abc";
str2 = "bcd";
std::cout << str1<<" "<<str2 << std::endl;
str1 = "efg";
str2 = "xyz";
std::cout << str1<<" "<<str2 << std::endl;
std::string str3;
str3 = str1 + str2;
std::cout<<str3<<std::endl;
str2 += "loveyou";
std::cout<<"str2 +="<<str2<<std::endl;
str2 += "dddd";
std::cout<<"str2 ++="<<str2<<std::endl;
if(str3 > "ccc")
{
std::cout<<"1 "<<std::endl;
}
else
{
std::cout<<"2 "<<std::endl;
}
if(str3 > str1)
{
std::cout<<"3 "<<std::endl;
}
else
{
std::cout<<"4 "<<std::endl;
}
system("pause");
return 0;
}
/*****complex.h虚数加减操作**************************************/
#ifndef COMPLEX_H
#define COMPLEX_H
class complex;
complex operator+(const complex a,const complex b);
class complex
{
friend complex operator+(const complex a,const complex b);
//友元运算符+重载
public:
complex(double r=0.0,double i=0.0);//数字的实部,虚部
void print();//打印这个复数
complex operator-(const complex &a);//成员函数运算符重载
private:
double real;
double imag;
};
#endif // COMPLEX_H
/******complex.cpp**************************************************/
#include "complex.h"
#include<iostream>
complex::complex(double r,double i)
{
real = r;
imag = i;
}
void complex::print()
{
std::cout<<"("<<real<<", "<<imag<<"i)"<<std::endl;
}
complex operator+(const complex a,const complex b)//+法运算函数
{
return complex(a.real+b.real,a.imag+b.imag);
}
complex complex::operator-(const complex &a)//成员函数运算符重载
{
return complex(this->real - a.real,this->imag - a.imag);
}
/**********main.cpp***************************************************/
#include <iostream>
#include"complex.h"
using namespace std;
int main()
{
complex c1(1.1,2.2),c2(3.3,4.4);
cout<< "c1 = " ;
c1.print();
cout<< "c2 = " ;
c2.print();
complex c3;
c3 = c1 + c2;//+法运算函数
cout<< "c3 = c1 + c2 == " ;
c3.print();
complex c4;
c4 = c2.operator -(c1);//减法成员函数运算符重载
cout<<"c4 = c2 - c1 == ";
c4.print();
return 0;
}
模板
使用模板的目的是能够让程序员编写与类型无关的代码,模板也是泛型编程的基础
函数模板:
参数类型不一样但是功能及函数名一致的函数:
template<class 形参名,class 形参名...>返回类型 函数名(参数列表)
{ 函数体; }
类模板:
成员属性的类型和成员函数的类型不一样,但是成员属性及函数一样
template<class 形参名,class 形参名...>class 类名
{ 函数体; }
类型形参仅由关键字class 或typename后接说明符构成
类模板的类型形参可以有默认值,函数模板的类型形参不能有默认值
#include <iostream>
using namespace std;
template<typename T>//定义一个模板函数
T add (T a, T b)
{
return a+b;
}
template<typename T = int,int max = 8>//类模板
class Array
{
public:
T getval(int i);
void setval(int i,T v);
private:
T ary[max];
};
template<typename T,int max>
T Array<T,max>::getval(int i)
{
return ary[i];
}
template<typename T,int max>
void Array<T,max>::setval(int i,T v)
{
ary[i] = v;
}
int main()
{
int a = 1,b = 2;
double c = 1.1,d = 2.2;
string str1 = "abc",str2 = "def";
cout<<add<int>(a,b)<<endl;
cout<<add<double>(c,d)<<endl;
cout<<add<string>(str1,str2)<<endl;
cout<<add(a,b)<<endl;//能够自动推导类型
cout<<add(c,d)<<endl;
cout<<add(str1,str2)<<endl;
Array<double,4> s1;
s1.setval(0,1.11);
cout<<s1.getval(0)<<endl;
Array<> s2;
s2.setval(0,999);
cout<<s2.getval(0)<<endl;
}