2007年10月19日星期五

关于C++类的拷贝构造函数


#include <iostream>

using namespace std;

class A
{
public:
A() {cout << "A()" << endl;}
A(const A& a){ cout << "A(const A& a)" << endl;}
void print() const {cout << "print in A" << endl;}
};

class B : public A
{
public:
B() {cout << "B()" << endl;}
B(const B& a){ cout << "B(const A& a)" << endl;}
void print() const {cout << "print in B" << endl;}
};

void printByValue(A a)
{
a.print();
}
void printByRef(const A& a)
{
a.print();
}
int main(int argc, char* argv[])
{
A a;
printByValue(a);
printByRef(a);
B b;
printByValue(b);
printByRef(b);
a = b;
printByValue(a);
printByRef(a);

return 0;
}

结果为:
A()
A(const A& a)
print in A
print in A
A()
B()
A(const A& a)
print in A
print in A
A(const A& a)
print in A
print in A

注:函数参数直接传值和“=”赋值均会调用拷贝构造函数。

2007年8月10日星期五

关于自定义类型名与变量名

今天看linux的源代码,居然发现一个地方自定义的类型名与变量名重名!试了一下,果然如此:
#include <iostream.h>

struct S
{
int i;
};
typedef int I;
class C
{
int j;
};

void main()
{
S S;
S.i = 4;

I I;
I = 4;
cout << I << endl;

C C;
}

以上程序是正确的,能通过编译。
另注:
若如此使用了,接下来的程序若再次用,比如说,类C定义变量,则必须像这样用:
class C cx;
而用
C cx;
则会报错!
(如果此时是typedef,而不是class或是struct,该怎么使用呢?)

2007年4月1日星期日

关于变量名空间的一小点

在C/C++中,变量名与(类名、结构名、typedef定义的类型名)属于不同的名空间。因此它们是可以相同的。如:
struct S { ... };
class C { ... };
typedef int INT;

S S; // right
C C; // right
INT INT = 3; // right

2007年3月21日星期三

重拾java(3):继承

❁java不支持多继承

❁子类不能访问父类中声明为private的类成员

❁java与C++中均一样,父类引用不能访问仅在子类中定义的函数或变量:
public class t2 {
public static void main(String args[]) {
F p = new S();
int i = p.s; // wrong
}
}

class F {
int f;
}

class S extends F {
int s;
}
❁super()必须永远是一个子类构造函数内执行的第一条语句。如下面的程序将会报错:
class S extends F {
int s;
S() {
int u;
super(); // Wrong! Constructor
call must be the first
statement in a constructor
int k;
}
}

因为构造函数按派生顺序,从超类到子类被调用。

❁如下程序例1。父类中的变量虽被覆盖,但p仍访问的是父类变量。而父类中的方法却被覆盖,因此p.pr()访问的是子类函数,类似于C++中的虚函数。这一点非常奇怪。因为在C++中,不管是变量还是函数,它们都是统一的,要覆盖就都会被覆盖,像这种动态的函数调用必须通过virtual来声明。另:C++中类中变量不能为virtual,只能是函数才可以用virtual修饰。这也许是java中要如此分开对待的原因吧。
public class t2 {
public static void main(String args[]) {
F p = new S();
System.out.println(p.i); // 1 父亲中的变量
p.pr(); // In S... 儿子的方法
}
}

class F {
int f;
int i;
F() {
;
}
void pr()
{ System.out.println("In F..."); }
}

class S extends F {
int s;
int i;
S() {
super.i = 1;
i = 2;
}
void pr()
{ System.out.println("In S..."); }
}

例2:
public class t2 {
public static void main(String args[]) {
F p1 = new S();
System.out.println(p1.i); // 1

S p2 = new S();
System.out.println(p2.i); // 2
}
}
class F {
int f;
int i;
F() {
;
}
}
class S extends F {
int s;
int i;
S() {
super.i = 1;
i = 2;
}
}
❁抽象abstract:
子类必须实现父类中所有的抽象函数。
包含一个或多个抽象方法的任何类也必须被声明为抽象的。
不能声明抽象构造函数或抽象静态方法。
抽象类虽不可被实例化,但可以被用来创建对象引用。
同C++中virtual类似,abstract不能用来修饰类中变量,而只能是函数。

重拾java(2)

1、类型
(1) 自动类型提升
byte a = 40;
byte b = 50;
byte c = 100;
int d = a * b / c; // a、b、c在计算中,
其类型自动提升为int型
System.out.println(d);

byte e = 5;
e = e * e; // wrong. Type mismatch: cannot
convert from int to byte
byte a = 64, b;
b = (byte)(a << 2);

一个浮点文字自动为double,如:
float ff = 3.55; //wrong
float f = 5 / 3 + 7.88; //wrong! Type mismatch:
cannot convert from
double to float

应该为:
float ff = 3.55F;
float f = 5 / 3 + 7.88f; //right!

2、数组
(1) 数组是作为对象实现的,因此有类内变量length:
int m[][] = new int[4][];
m[0] = new int[5];
m[1] = new int[8];
System.out.println("m[][] = " + m.length); // 4
System.out.println("m[0] = " + m[0].length); // 5
//System.out.println("m[1][] = " + m[1][].length);
// wrong
//System.out.println("m[1][3] = " + m[1][3].length);
// wrong
//System.out.println("m[3] = " + m[3].length);
// wrong

(2) 数组越界:运行时错误(C/C++不提供运行时边界检查)
int m[] = new int[2];
m[0] = 0;
m[1] = 1;
m[2] = 2; //wrong

(3) 同C/C++,“int n[] = { 1, 2, 3, 4, };” 是正确的(即末尾可多一个逗号)。

(4) java支持多维不等数组。如:
int twoD[][] = new int[3][];   // 仅需第一维指定内存
twoD[0] = new int[5];
twoD[1] = new int[3];
twoD[2] = new int[1];
3、运算符
(1) java中,“%”运算符可以用于浮点类型,而C/C++不能。如:
double d = 22.33 % 2.34;
(2) >> :向右移 >>> :向右移,左补零
byte类型的值最高位若为1,则>>>对其是不可能的。如:
byte b = 0xf1; 则 b>>4为0xff,b>>>4为0xff,(b & 0xff)>>4为0x0f。

(3) java中对boolean类型的值来说有运算符&(逻辑与)与&&(短路与)的区分(|与||同)。

(4)
float f = 3;
int i = 3;
if(i == f)
  System.out.println("Yes"); // This will be printed
else
  System.out.println("No");

4、程序控制语句
(1) 在程序控制语句中,除了for语句可以使用逗号作为分隔符外,其他的地方不能使用逗号。如:
int k = 0;
int i = true ? (k=3,k) : (k=4,k);
// 错误,Syntax error on token
",", invalid AssignmentOperator
这一点与C/C++不同。

(2) switch语句中,若default语句不在最后,其后也应该跟一个break语句。如:
int i = 4;
int k = 0;
switch(i)
{
  default :
    k = 4;
  case 1:
    k = 1;
    break;
  case 2:
    k = 2;
    break;
  case 3:
    k = 3;
    break;
}
System.out.println(k);

将打印出“k=1”。

(3) java中break、continue后可以跟一个标号。如:
one:for(int j=0;j<100;j++)
{
if(j==10) break one;
System.out.println(j + " ");
}
而在C/C++中不可以。

(4) 像“7 + 8;”这样的无意义的语句在java中是不允许的;而C/C++中可以。

5、函数
(1) 关于重载,java与C++中都是允许的,而在C中是不允许的。
在C++中,看下面这个例子:
#include <iostream.h>

//函数1
//void f()
//{ printf("haha1\n"); }

//函数2
void f(int i)
{ printf("haha2\n"); }

int main()
{
int g = (int)f; //(*)
((void (*)())g)();

return 0;
}
若不将函数1、2中的一个注释掉,赋值语句(*)处将会报错,因为在这里产生了二义。而只留有一个函数即没有重载,就没有事。

6、类
(1) java中默认为近似public的属性,而C++中为private。

(2) 关于类实例的声明:
java中:
X x = new X();  // X x = new X;的形式是错误的
x.i = 0;
C++中:
X x = new X(); 或 X x = new X;
x->i = 0;

X x; // 若没有构造函数或构造函数无参数,则 X x();的形式是错的的
x.i = 0;
(3) C++中类定义结尾必须有分号,而java可有可无。

(4) java中finalize方法与C++的析构函数是有区别的。protected void finalize()函数仅在垃圾收集时被调用,而当一个对象在作用域外时,她是不会被调用的。
这意味着你不知道什么时候或是否会执行finalize()。因此,你的程序必须提供其他的方法来释放被对象使用的系统资源。
这与C++是不同的。C++的析构函数在对象处于作用域外时被调用。
java中因为所有的对象是使用new动态分配的,因此不需要担心关于对象在作用域范围之外的问题。因为对象创建时所在的方法终止了,
对象将继续存在,只要在你的程序中的某处存在一个此对象的引用。当不存在它的引用时,下次垃圾收集起作用时,将重新收回这个对象。

(5) 在C++中,如下:
class X {
public:
int i;
};

int main()
{
X x();

return 0;
}
此x将被认为是函数的声明。如:
class X {
public:
int i;
};

int main()
{
X x();
x();
return 0;
}

X x()
{
cout << "haha\n":
...
}
(6) 在函数中的参数,简单类型是按值传递,而对象则按引用传递

(7) 关于类中的static
声明为static的方法有几条限制:
─它们仅可以调用其他static方法;
─它们只能访问static数据;
─它们不能以任何方式引用this或super。
关于static块,这个块仅在该类被第一次加载时执行一次。例:
public class t2 {
static int i;
public static void main(String args[]) {
System.out.println("Start in main");

Y y = new Y();

System.out.println("End in main");

y = null;
}

}

class Y {
int i;
static int a = 3;
static int b;
static {
System.out.println("Static block");
b = a * 4;
}
Y() {
System.out.println("Con...");
}
protected void finalize() {
System.out.println("decon...");
}
}
打印:
Start in main
Static block
Con...
End in main

(8) 关于类嵌套(C++中没有)
A:外壳类 B:核类
A可知B,而A外的作用域不可知B。
B可访问A的成员,包括私有成员;A不能访问B的成员。
B可分为静态的(修饰以static)和非静态的(内部类)。静态的B必须通过一个对象来访问A的成员,而不能直接引用之;而非静态类则可以
访问A的所有成员,并且可以像A的其他非静态成员那样以同样的方式直接引用它们。
另外,嵌套类可以定义在任何块内,比如函数、for循环内等等。

(9) String
String ss = "wre"+"fsd";  // 对
String ss = "wre""fsd"; // 错,虽然在
C/C++中可以这样
(10) 与C/C++不同,java的命令行参数计数不是从所运行的程序名开始的。如:
class CommandLine {
public static void main(String args[])
{
for(int i=0;i<args.length;i++)
{
System.out.println("args["+i+"]:"+args[i]);
}
}
执行 # java CommandLine this is a test 100 -1
将打印出
args[0]:this
args[1]:is
args[2]:a
args[3]:test
args[4]:100
args[5]:-1

2007年3月20日星期二

不用循环和递归,用C打印1~999

同学出了rt的这道题。没作出来。看看答案,果然厉害,让我想起linux内核源代码中的一大堆的define扩展。
答案:
#define A(x) x;x;x;x;x;x;x;x;x;x;
int main ()
{
int i = 1;
A(A(A(printf("%d", i++))));
}