C++初始化变量有好几种方法:
int a=0;
int a={0};
int a{0};
如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此时,变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。
如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显式地初始化变量(任何包含了显式初始化的声明即成为定义。):
extern
int
(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化,最好也初始化,否则容易出错)。
(2)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
它的作用是为变量起一个别名。假如有一个变量a,想给它起一个别名b,可以这样写:
int a;
int &b=a;
int *p=
这就表明了b是a的“引用”,即a的别名。经过这样的声明,使用a或b的作用相同,都代表同一变量。
int &b=a2;//企图使b变成a2的别名(引用)是不行的。这样是错误的。
引用不是一个对象,所以不能定义引用的引用。
理解这些声明的技巧在于,查看关键字const右边来确定什么被声明为常量
例如const int *a = int *b;
C++ 11中引入的auto主要有两种用途:自动类型推断和返回值占位。auto在C++ 98中的标识临时变量的语义,由于使用极少且多余,在C++ 11中已被删除。
auto自动类型推断,用于从初始化表达式中推断出变量的数据类型。通过auto的自动类型推断,可以大大简化我们的编程工作。下面是一些使用auto的例子。
auto
auto
auto
auto
auto
vector<</span>int>
auto
auto
另外,在使用模板技术时,如果某个变量的类型依赖于模板参数,不使用auto将很难确定变量的类型(使用auto后,将由编译器自动进行确定)。下面是一个具体的例子。
template
void
{
auto
}
auto返回值占位,主要与decltype配合使用,用于返回值类型后置时的占位。
template
auto
{
typedef
NewType
return
}
至于为什么需要将返回值类型后置,这里简单说明一下。如果没有后置,则函数声明为decltype(t*u) Multiply(T t, U u),但此时模板参数t和u还未声明,编译无法通过。
如果这个表达式是个函数,decltype 给出的类型为函数返回值的类型。
[cpp]
int
decltype(add(5,6))
非常重要的标记一下,decltype 不会执行表达式而auto会,他仅仅推论一下表达式的类型。
int
decltype(
string s1="hello", s2="world";
string s3=s1+","+s2;
string s4 =s1+",";
string s4 =s1+s2;
而+两侧运算对象至少一个是string类型:
string s6="hello"+","+s2; //错误, 不能把字面值直接相加
基于范围的for 语句
遍历给定序列中每个元素并操作
简单例子:把string对象中的字符每行一个个的输出出来
string str("some strings");
for (auto c : str)
cout<<c<<endl;
这里使用auto 让编译器来决定c变量的类型,这里是char型
但如果想改变string 对象中的字符值,循环变量必须定义成引用
比如将string对象字符全变为大写
string str("some strings");
for (auto &c : str)
c=toupper(c);
cout<<s<<endl;
这里toupper函数是定义在〈cctype〉头文件中的处理string中的某个特定字符的集合。
标准模板库类型vector 表对象的集合。一般用{
vector
除了以下情况用()初始化
vector
vector
vector
如果这时使用{}
vector
表示1个int类型元素,该元素是10。
vector
它的成员函数push_back()
vector
for(int i=0; i!=100; ++i)
v2.push_back(i);
迭代器
vector::iterator iter;这条语句定义了一个名为iter的变量,它的数据类型是由vector定义的iterator类型。
(2) 使用迭代器读取vector中的每一个元素:
vector 《int》 ivec(10,1);
for(vector::iterator iter=ivec.begin();iter!=ivec.end();++iter)
{
*iter=2; //使用 * 访问迭代器所指向的元素
}
const_iterator:
只能读取容器中的元素,而不能修改。
for(auto it=ivec.cbegin();it!=ivec.cend();it++)
{
cout<<*it;
}
解引用操作符:所有迭代器都提供了解引用操作符(*),用于获取迭代器所指向的元素。以下代码都是合法的。
std::cout << *iter; |
取后继元素操作符:所有迭代器都可以通过 iter++、++iter 操作符获取其后继元素的迭代器。
auto pbeg=v.begin();
while(pbeg!=v.end() && *pbeg>=0)
cout<<*pbeg++<<endl;
不过注意,C++中一般更倾向于使用前置++, 因为这样先改变指针再返回指针改变后所指向的值,节省空间并且更易理解。
多维数组的初始化
允许使用花括号括起来的一组值初始化多维数组,这点和普通的数组一样。下面的初始化形式中,多维数组的每一行分别用花括号括了起来:
int ia[3][4] = {
};
其中内层嵌套着的花括号并非必需的,例如下面的初始化语句,形式上更为简洁,完成的功能和上面这段代码完全一样:
// 没有标识每行的花括号,与之前的初始化语句是等价的
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
constexpr size_t rowCnt = 3, colCnt = 4;
int ia[rowCnt][colCnt]; // 12 个未初始化的元素
// 对于每一行
for (size_t i = 0; i != rowCnt; ++i) {
}
外层的for循环遍历ia的所有元素,注意这里的元素是一维数组;内层的for循环则遍历那些一维数组的整数元素。此例中,我们将元素的值设为该元素在整个数组中的序号。
使用范围 for语句处理多维数组
由于在C++11新标准中新增了范围for语句,所以前一个程序可以简化为如下形式:
size_t cnt = 0;
for (auto &row : ia)
}
因为要改变元素的值,所以得把控制变量row和col声明成引用类型。
标准库函数 begin和end
int ia[] = {0,1,2,3,4,5,6,7};
int *beg = begin(ia); //指向ia的首元素
int *last = end(ia);
位与,位或,位异或
7&8 = 0000 0111 & 0000 1000 = 0000 0000 = 0
7|8 = 0000 0111 | 0000 1000 = 0000 1111 = 15
7^8 = 0000 0111 ^ 0000 1000 = 0000 0111 = 7
switch(ch){
case 'a':
case 3:
}
case 后面只能是整型常量表达式(整数,字母等),不能是小数或者变量。
try
{
//程序中抛出异常
throw value;
}
catch(valuetype v)
{
//例外处理程序段
}
语法小结:throw抛出值,catch接受,当然,throw必须在“try语句块”中才有效。
int
{
try
{
cout
throw
//程序如果执行到下面这句表示还没有碰到
cout
throw
}
catch(
{
cout
}
catch(
{
cout
}
return
}
http://blog.sina.com.cn/s/blog_a9303fd901018ost.html
建议函数和变量在头文件中声明,在源文件中定义。
对于不需要修改的变量,使用常量引用来传递参数可以既保证安全性又保证效率!
调用一个返回引用的函数得到左值
函数返回引用类型时,没有复制返回值。相反,返回的是对象本身。
返回指向函数调用前就已经存在的对象的引用是正确的。
我们能为返回类型是非常量引用的函数的结果赋值:
{
return str[ix];
}
int main()
{
string s("a value"); //初始化
get_val(s,0)='A';
cout<<s<<endl;
}
const_cast
去掉const属性:const_cast 《int *》 (&num),常用,因为不能把一个const变量直接赋给一个非const变量,必须要转换。
把const的num地址转化为普通int 地址
const int ica = 100;
都是100
使用范围:
1. 常量指针被转化成非常量指针,转换后指针指向原来的变量
- const
int ica = 100; -
int * ia = const_cast<</span>int *>(&ica); -
*ia = 200; -
cout<< *ia <<ica<<endl; //200 100
-
const int i = 100; -
int j = const_cast<</span>int>(i); //不允许
默认实参是通过给形参表中的形参提供明确的初始值来指定的。程序员可为一个或
既可以在函数声明中也可以在函数定义中指定默认实参,但是在一个文件中,只能为一个形参指定默认实参一次;
个人比较推荐这种格式: 在申明中将默认参数给出,在定义中不再给出!
- //CPerson.h
- int
initPerson(int old, string name = "二毛", string race = "汉"); - //CPerson.cpp
- #include
"CPerson.h" - //int
initPerson(int old, string name = "二毛", string race = "汉"){...} -
- int
initPerson(int old, string name, string race) //定义 - {...}
构造函数用来初始化类的对象。只要类的对象被创建,就会执行构造函数。构造函数的名字和类名相同。构造函数没有返回类型。
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。例如:
public:
} ;
上面的例子中两个构造函数的结果是一样的。
也可以在类的外部定义构造函数
如果没有为一个类显式地定义任何构造函数,编译器将自动为这个类生成默认构造函数。
1.什么是默认构造函数?
一个函数是默认构造函数当且仅当调用它可以不需要传入任何参数。这个函数可以是用户自定义的,也可以是编译器产生的。
2.编译器什么时候隐式声明默认构造函数?
有两个条件:
- 该类没有显式声明任何构造函数。--既然你都定义了,系统就不给你生成了。
- 1、每个类必须有一个构造函数,否则没法创建对象;
- 2、若programer没有提供任何构造函数,则C++提供一个默认的构造函数,该默认构造函数是无参构造函数,它仅负责创建对象,不做任何初始化的工作;
3、只要programer定义了一个构造函数(不管是无参还是有参构造),C++就不再提供默认的默认构造函数。即如果为类定义了一个带参的构造函数,还想要无参构造函数,就必须自己定义;
4、与变量定义类似,在用默认构造函数创建对象时,如果创建的是全局对象或静态对象,则对象的位模式全为0,否则,对象值是随机的。
默认情况下,struct的保护级别为public,而class的保护级别为private。
友元 :优点:提高了程序的运行效率。
缺点:破坏了类的封装性和数据的透明性。
友元函数
class Point {
public: Point(double xx, double yy)
{ x=xx; y=yy; }
void Getxy();
friend double Distance(Point &a, Point &b);
private: double x, y;
};
void Point::Getxy() {
cout<<"("<<<","<<Y<<")"<<endl; }
double Distance(Point &a, Point &b)
{
double dx = a.x -b.x; double dy = a.y - b.y; return sqrt(dx*dx+dy*dy);
}
void main() { Point p1(3.0, 4.0), p2(6.0, 8.0);
p1.Getxy(); p2.Getxy();
double d = Distance(p1, p2);
cout<<"Distance is"<< endl; }
注意友元函数Distance的调用,像普通成员那样调用,因为它不属于类。所以不像p1.Getxy();前面要带对象.
友元类
在类中,静态成员可以实现多个对象之间的数据共享
初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆。
初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员
* 静态成员函数与普通成员函数的根本区别是,静态成员函数没有this指针。因而静态成员函数可直接访问本类中的静态数据成员,对于非静态数据成员,一定要加上“对象名.”。
非静态成员函数可以任意地访问静态成员函数和静态数据成员。
静态成员函数不能直接访问非静态成员函数和非静态数据成员,需要通过对象调用
#include
using namespace std;
class Student
{
};
float Student::total=0;
void Student::printinfo()
{
}
void Student::printaver(Student* ps)
{
}
int main()
{
}
This
当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。
this指针只能在一个类的成员函数中调用,它表示当前对象的地址。下面是一个例子:
void Date::setMonth( int mn ) { month = mn; // 这三句是等价的 this->month = mn; (*this).month = mn; } 1. this只能在成员函数中使用。 全局函数,静态函数都不能使用this。
this在成员函数的开始执行前构造,在成员的执行结束后清除。
我们只有获得一个对象后,才能通过对象使用this指针
在以下场景中,经常需要显式引用this指针
7. 举例:
//例1:
#include
#include
class Person {
};
void TestPerson(void)
{
}
int main(void)
{
}
//例2:
#include
class Location {
};
void Location::assign(Location& pointer)
{
//同一对象之间的赋值没有意义,所以要保证pointer不等于this, this 是当前对象的地址,pointer是当前类对象中调用的
//另一个对象的地址参数
}
int main()
{
}
动态数组
c++要求定义数组时,必须明确给定数组的大小,要不然编译通不过
最后 ,因为调用了new, 千万千万别忘记在用完之后,将其所占资源 delete 掉.delete动态数组的时候,在指针前加 []
delete []
拷贝构造函数:
下面
http://blog.csdn.net/lwbeyond/article/details/6202256
我摘抄一段过来
一. 什么是拷贝构造函数
首先对于普通类型的对象来说,它们之间的复制是很简单的,例如:
- int
a = 100; - int
b = a;
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。
下面看一个类对象拷贝的简单例子。
用已有对象初始化新建对象
- #include
- using
namespace std; -
- class
CExample { - private:
-
int a; - public:
-
//构造函数 -
CExample(int b) -
{ a = b;} -
-
//拷贝构造函数 -
CExample(const CExample& C) -
{ -
a = C.a; -
} -
-
//一般函数 -
void Show () -
{ -
cout<<a<<endl; -
} - };
-
- int
main() - {
-
CExample A(100); -
CExample B = A; // CExample B(A); 也是一样的 -
B.Show (); -
return 0; - }
运行程序,屏幕输出100。从以上代码的运行结果可以看出,系统为对象 B 分配了内存并完成了与对象 A 的复制过程。就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。
CExample(const CExample& C) 就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。
函数调用时参数是对象
- class
CExample - {
- private:
-
int a; -
- public:
-
//构造函数 -
CExample(int b) -
{ -
a = b; -
cout<<"creat: "<<a<<endl; -
} -
-
//拷贝构造 -
CExample(const CExample& C) -
{ -
a = C.a; -
cout<<"copy"<<endl; -
} -
-
//析构函数 -
~CExample() -
{ -
cout<< "delete: "<<a<<endl; -
} -
-
void Show () -
{ -
cout<<a<<endl; -
} - };
-
- //全局函数,传入的是对象
- void
g_Fun(CExample C) - {
-
cout<<"test"<<endl; - }
-
- int
main() - {
-
CExample test(1); -
//传入对象 -
g_Fun(test); -
-
return 0; - }
调用g_Fun()时,会产生以下几个重要步骤:
(1).test对象传入形参时,会先会产生一个临时变量,就叫 C 吧。
(2).然后调用拷贝构造函数把test的值给C。 整个这两个步骤有点像:CExample C(test);
(3).等g_Fun()执行完后, 析构掉 C 对象。